3e1b1620bff90ad8b1980a609f1e3c2936c86dcc
[appc.git] / appc-oam / appc-oam-bundle / src / main / java / org / openecomp / appc / oam / AppcOam.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * APPC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * Copyright (C) 2017 Amdocs
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  * 
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  * 
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  * ECOMP is a trademark and service mark of AT&T Intellectual Property.
21  */
22
23 package org.openecomp.appc.oam;
24
25 import org.openecomp.appc.Constants;
26 import org.openecomp.appc.configuration.Configuration;
27 import org.openecomp.appc.configuration.ConfigurationFactory;
28 import org.openecomp.appc.exceptions.APPCException;
29 import org.openecomp.appc.executor.objects.Params;
30 import org.openecomp.appc.i18n.Msg;
31 import org.openecomp.appc.logging.LoggingConstants;
32 import org.openecomp.appc.logging.LoggingUtils;
33 import org.openecomp.appc.metricservice.MetricRegistry;
34 import org.openecomp.appc.metricservice.MetricService;
35 import org.openecomp.appc.metricservice.metric.Metric;
36 import org.openecomp.appc.requesthandler.LCMStateManager;
37 import org.openecomp.appc.requesthandler.RequestHandler;
38 import com.att.eelf.configuration.EELFLogger;
39 import com.att.eelf.configuration.EELFManager;
40 import com.att.eelf.i18n.EELFResourceManager;
41 import com.google.common.util.concurrent.Futures;
42 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
43 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
44 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
45 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
46 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.*;
47 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.common.header.CommonHeader;
48 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.get.metrics.output.Metrics;
49 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.get.metrics.output.MetricsBuilder;
50 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.get.metrics.output.metrics.KpiValues;
51 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.get.metrics.output.metrics.KpiValuesBuilder;
52 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.status.Status;
53 import org.opendaylight.yang.gen.v1.org.openecomp.appc.oam.rev170303.status.StatusBuilder;
54 import org.opendaylight.yangtools.yang.common.RpcError;
55 import org.opendaylight.yangtools.yang.common.RpcResult;
56 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
57 import org.osgi.framework.Bundle;
58 import org.osgi.framework.BundleContext;
59 import org.osgi.framework.FrameworkUtil;
60 import org.osgi.framework.ServiceReference;
61 import org.slf4j.MDC;
62
63 import java.net.InetAddress;
64 import java.util.*;
65 import java.util.concurrent.*;
66
67 import org.openecomp.appc.oam.messageadapter.*;
68
69
70 import static com.att.eelf.configuration.Configuration.*;
71
72 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
73 import org.osgi.framework.BundleContext;
74 import org.osgi.framework.FrameworkUtil;
75 import org.osgi.framework.ServiceReference;
76
77 import java.util.ArrayList;
78 import java.util.List;
79 import java.util.Map;
80 import java.util.Properties;
81 import java.util.concurrent.Executors;
82 import java.util.concurrent.Future;
83
84
85 public class AppcOam implements AutoCloseable, AppcOamService {
86
87     private Configuration configuration = ConfigurationFactory.getConfiguration();
88     private final EELFLogger logger = EELFManager.getInstance().getLogger(AppcOam.class);
89
90     private boolean isMetricEnabled = false;
91
92
93     private final ScheduledExecutorService scheduledExecutorService;
94
95     private volatile ScheduledFuture<?> outstandingLCMRequestMonitorSheduledFuture;
96
97
98     private MessageAdapter messageAdapter;
99
100
101     /**
102      * The ODL data store broker. Provides access to a conceptual data tree store and also provides the ability to
103      * subscribe for changes to data under a given branch of the tree.
104      */
105     private DataBroker dataBroker;
106
107     /**
108      * ODL Notification Service that provides publish/subscribe capabilities for YANG modeled notifications.
109      */
110     private NotificationProviderService notificationService;
111
112     /**
113      * Provides a registry for Remote Procedure Call (RPC) service implementations. The RPCs are defined in YANG models.
114      */
115     private RpcProviderRegistry rpcRegistry;
116
117     /**
118      * Represents our RPC implementation registration
119      */
120     private BindingAwareBroker.RpcRegistration<AppcOamService> rpcRegistration;
121
122
123     /**
124      * The yang rpc names
125      */
126     public enum RPC {
127         start,
128         stop,
129         ;
130     }
131
132
133     /**
134      * @param dataBroker
135      * @param notificationProviderService
136      * @param rpcProviderRegistry
137      */
138     @SuppressWarnings({
139             "javadoc", "nls"
140     })
141     public AppcOam(DataBroker dataBroker, NotificationProviderService notificationProviderService,
142                    RpcProviderRegistry rpcProviderRegistry) {
143
144         String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
145         logger.info(Msg.COMPONENT_INITIALIZING, appName, "oam");
146
147         this.dataBroker = dataBroker;
148         this.notificationService = notificationProviderService;
149         this.rpcRegistry = rpcProviderRegistry;
150
151         if (this.rpcRegistry != null) {
152             rpcRegistration = rpcRegistry.addRpcImplementation(AppcOamService.class, this);
153         }
154
155         Properties properties = configuration.getProperties();
156         if (properties != null && properties.getProperty("metric.enabled") != null) {
157             isMetricEnabled = Boolean.valueOf(properties.getProperty("metric.enabled"));
158         }
159
160
161         messageAdapter = new MessageAdapter();
162         messageAdapter.init();
163
164
165         scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
166                 new ThreadFactory(){
167
168                     @Override
169                     public Thread newThread(Runnable runnable) {
170                         Bundle bundle = FrameworkUtil.getBundle(AppcOam.class);
171                         return new Thread(runnable,bundle.getSymbolicName() + " scheduledExecutor");
172                     }
173                 }
174         );
175
176         logger.info(Msg.COMPONENT_INITIALIZED, appName, "oam");
177     }
178
179     /**
180      * Implements the close of the service
181      *
182      * @see AutoCloseable#close()
183      */
184     @SuppressWarnings("nls")
185     @Override
186     public void close() throws Exception {
187         String appName = configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
188         logger.info(Msg.COMPONENT_TERMINATING, appName, "oam");
189         scheduledExecutorService.shutdown();
190         if (rpcRegistration != null) {
191             rpcRegistration.close();
192         }
193         logger.info(Msg.COMPONENT_TERMINATED, appName, "oam");
194     }
195
196     @Override
197     public Future<RpcResult<GetMetricsOutput>> getMetrics() {
198
199         GetMetricsOutputBuilder outputBuilder = new GetMetricsOutputBuilder();
200
201         if (!isMetricEnabled){
202             logger.error("Metric Service not enabled returning failure");
203             RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput> status(false).withError(RpcError.ErrorType.APPLICATION,"Metric Service not enabled").build();
204             return Futures.immediateFuture(result);
205         }
206
207         MetricService metricService = null;
208         try {
209             metricService = getService(MetricService.class);
210         } catch (APPCException e){
211             logger.error("MetricService not found",e);
212             RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput> status(false).withError(RpcError.ErrorType.APPLICATION,"Metric Service not found").build();
213             return Futures.immediateFuture(result);
214         }
215         Map<String,MetricRegistry> allMetricRegitry = metricService.getAllRegistry();
216
217         if(allMetricRegitry == null || allMetricRegitry.isEmpty()){
218             logger.error("No metrics registered returning failure");
219             RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput> status(false).withError(RpcError.ErrorType.APPLICATION,"No metrics Registered").build();
220             return Futures.immediateFuture(result);
221         }
222         List<Metrics> metricsList = new ArrayList<>();
223
224         logger.debug("Iterating metric registry list");
225         for (MetricRegistry metricRegistry :  allMetricRegitry.values() ) {
226             logger.debug("Iterating metric registry :" + metricRegistry.toString());
227             Metric[] metrics = metricRegistry.metrics() ;
228             if(metrics!= null && metrics.length >0) {
229                 logger.debug("Iterating though metrics in registry");
230                 for (Metric metric : metrics) {
231                     logger.debug("Iterating though metrics: "+ metric.name());
232                     MetricsBuilder metricsBuilder = new MetricsBuilder();
233                     metricsBuilder.setKpiName(metric.name());
234                     metricsBuilder.setLastResetTime(metric.getLastModified());
235                     List<KpiValues> kpiList = new ArrayList<>();
236                     Map<String, String> metricsOutput = metric.getMetricsOutput();
237                     for (Map.Entry<String, String> kpi : metricsOutput.entrySet()) {
238                         KpiValuesBuilder kpiValuesBuilder = new KpiValuesBuilder();
239                         kpiValuesBuilder.setName(kpi.getKey());
240                         kpiValuesBuilder.setValue(kpi.getValue());
241                         kpiList.add(kpiValuesBuilder.build());
242                     }
243                     metricsBuilder.setKpiValues(kpiList);
244                     metricsList.add(metricsBuilder.build());
245                 }
246             }
247         }
248         outputBuilder.setMetrics(metricsList);
249         RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput> status(true).withResult(outputBuilder.build()).build();
250         return Futures.immediateFuture(result);
251     }
252
253     @Override
254     public Future<RpcResult<StopOutput>> stop(StopInput stopInput){
255         logger.debug("Input received : " + stopInput);
256         final Date startTime = new Date();
257         Status status = this.buildStatus(OAMCommandStatus.ACCEPTED);
258         final  CommonHeader commonHeader = stopInput.getCommonHeader();
259
260         try {
261             setInitialLogProperties(commonHeader,RPC.stop);
262
263             //Close the gate so that no more new LCM request will be excepted.
264             LCMStateManager lcmStateManager = getService(LCMStateManager.class);
265             lcmStateManager.disableLCMOperations();
266             //Begin monitoring outstanding LCM request
267             scheduleOutstandingLCMRequestMonitor(commonHeader,startTime);
268         } catch(Throwable t) {
269             status = unexpectedOAMError(t,RPC.stop);
270         }
271         finally {
272             LoggingUtils.auditWarn(startTime.toInstant(),
273                     new Date(System.currentTimeMillis()).toInstant(),
274                     String.valueOf(status.getCode()),
275                     status.getMessage(),
276                     this.getClass().getCanonicalName(),
277                     Msg.OAM_OPERATION_STOPPING,
278                     getAppcName()
279             );
280             this.clearRequestLogProperties();
281         }
282
283         StopOutputBuilder stopOutputBuilder = new StopOutputBuilder();
284         stopOutputBuilder.setStatus(status);
285         stopOutputBuilder.setCommonHeader(commonHeader);
286         StopOutput stopOutput = stopOutputBuilder.build();
287         return RpcResultBuilder.success(stopOutput).buildFuture();
288     }
289
290     @Override
291     public Future<RpcResult<StartOutput>> start(StartInput startInput){
292         logger.debug("Input received : " + startInput);
293         final Date startTime = new Date();
294         Status status = this.buildStatus(OAMCommandStatus.ACCEPTED);
295         final CommonHeader commonHeader = startInput.getCommonHeader();
296
297         try {
298
299
300             setInitialLogProperties(commonHeader,RPC.start);
301
302             this.scheduleStartingAPPC(commonHeader,startTime);
303         } catch(Throwable t) {
304             status = unexpectedOAMError(t,RPC.start);
305         }
306         finally {
307             LoggingUtils.auditWarn(startTime.toInstant(),
308                     new Date(System.currentTimeMillis()).toInstant(),
309                     String.valueOf(status.getCode()),
310                     status.getMessage(),
311                     this.getClass().getCanonicalName(),
312                     Msg.OAM_OPERATION_STARTING,
313                     getAppcName()
314             );
315             this.clearRequestLogProperties();
316         }
317
318         StartOutputBuilder startOutputBuilder = new StartOutputBuilder();
319         startOutputBuilder.setStatus(status);
320         startOutputBuilder.setCommonHeader(commonHeader);
321         StartOutput startOutput = startOutputBuilder.build();
322         return RpcResultBuilder.success(startOutput).buildFuture();
323     }
324
325     private <T> T getService(Class<T> _class) throws APPCException {
326         BundleContext bctx = FrameworkUtil.getBundle(_class).getBundleContext();
327         ServiceReference sref = bctx.getServiceReference(_class.getName());
328         if (sref != null) {
329             if(logger.isTraceEnabled()) {
330                 logger.debug("Using the BundleContext to fetched the service reference for " + _class.getName());
331
332             }
333             return (T) bctx.getService(sref);
334         } else {
335             throw new APPCException("Using the BundleContext failed to to fetch service reference for " + _class.getName());
336         }
337     }
338
339     private Status buildStatus(OAMCommandStatus osmCommandStatus){
340         StatusBuilder status = new StatusBuilder();
341         status.setCode(osmCommandStatus.getResponseCode());
342         status.setMessage(osmCommandStatus.getResponseMessage());
343         return status.build();
344     }
345
346     private Status buildStatus(OAMCommandStatus osmCommandStatus,Params params){
347         StatusBuilder status = new StatusBuilder();
348         status.setCode(osmCommandStatus.getResponseCode());
349         status.setMessage(osmCommandStatus.getFormattedMessage(params));
350         return status.build();
351     }
352
353
354
355     private void clearRequestLogProperties() {
356         try {
357             MDC.remove(MDC_KEY_REQUEST_ID);
358             MDC.remove(MDC_SERVICE_INSTANCE_ID);
359             MDC.remove(MDC_SERVICE_NAME);
360             MDC.remove(LoggingConstants.MDCKeys.PARTNER_NAME);
361             MDC.remove(LoggingConstants.MDCKeys.TARGET_VIRTUAL_ENTITY);
362         } catch (Exception e) {
363
364         }
365     }
366
367     private void setInitialLogProperties(CommonHeader commonHeader,RPC action) {
368
369         try {
370             MDC.put(MDC_KEY_REQUEST_ID, commonHeader.getRequestId());
371             MDC.put(LoggingConstants.MDCKeys.PARTNER_NAME, commonHeader.getOriginatorId());
372             MDC.put(MDC_INSTANCE_UUID, ""); // value should be created in the future
373             try {
374                 MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getCanonicalHostName()); //Don't change it to a .getHostName() again please. It's wrong!
375                 MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress());
376                 MDC.put(LoggingConstants.MDCKeys.SERVER_NAME, InetAddress.getLocalHost().getHostName());
377                 MDC.put(MDC_SERVICE_NAME, action.name());
378             } catch (Exception e) {
379                 logger.debug("MDC constant error",e);
380             }
381         } catch (RuntimeException e) {
382             //ignore
383         }
384     }
385
386
387     private void storeErrorMessageToLog(Status status, String additionalMessage) {
388         LoggingUtils.logErrorMessage(
389                 String.valueOf(status.getCode()),
390                 status.getMessage(),
391                 LoggingConstants.TargetNames.APPC,
392                 LoggingConstants.TargetNames.APPC_OAM_PROVIDER,
393                 additionalMessage,
394                 this.getClass().getCanonicalName());
395     }
396
397     private String getAppcName(){
398         return configuration.getProperty(Constants.PROPERTY_APPLICATION_NAME);
399     }
400
401     private Status unexpectedOAMError(Throwable t,RPC action){
402         final String appName = getAppcName();
403
404         String exceptionMessage = t.getMessage() != null ? t.getMessage() : t.toString();
405
406         String errorMessage = EELFResourceManager.format(Msg.OAM_OPERATION_EXCEPTION, t, appName, t.getClass().getSimpleName(), action.name(), exceptionMessage);
407
408         Params params = new Params().addParam("errorMsg", exceptionMessage);
409         Status status = buildStatus(
410                 OAMCommandStatus.UNEXPECTED_ERROR,
411                 params
412         );
413
414         storeErrorMessageToLog(status,errorMessage);
415         return status;
416     }
417
418
419     private int getInprogressLCMRequestCount() throws APPCException {
420         RequestHandler requestHandler = getService(RequestHandler.class);
421
422         if(requestHandler == null) {
423             return 0;
424         }
425
426         int inprogressRequestCount = requestHandler.getInprogressRequestCount();
427         return inprogressRequestCount;
428     }
429
430
431
432     private void scheduleOutstandingLCMRequestMonitor(final CommonHeader commonHeader,final Date startTime){
433
434
435         class MyCommand implements Runnable{
436
437             public ScheduledFuture<?> myScheduledFuture = null;
438
439             @Override
440             public void run() {
441                 try {
442                     setInitialLogProperties(commonHeader, RPC.stop);
443
444
445                     logDebug("Executing stopping task ");
446
447                     ScheduledFuture<?> currentScheduledFuture = AppcOam.this.outstandingLCMRequestMonitorSheduledFuture;
448
449                     //cancel myself if I am not the current outstandingLCMRequestMonitor
450                     if(currentScheduledFuture != myScheduledFuture){
451                         myScheduledFuture.cancel(false);
452                         return;
453                     }
454
455                     Status status = buildStatus(OAMCommandStatus.SUCCESS);
456
457
458                     try {
459
460                         //log status and return if there are still LCM request in progress
461                         int inprogressRequestCount = getInprogressLCMRequestCount();
462                         if (inprogressRequestCount > 0) {
463                             logDebug("The application '%s' has '%s' outstanding LCM request to complete before coming to a complete stop.  ",
464                                                 getAppcName(),
465                                                 inprogressRequestCount
466                                         );
467                             return;
468                         }
469
470                     } catch (Throwable t) {
471                         status = unexpectedOAMError(t, RPC.stop);
472                         myScheduledFuture.cancel(false);
473                     }
474
475                     try {
476                         OAMContext oamContext = new OAMContext();
477                         oamContext.setRpcName(RPC.stop);
478                         oamContext.setCommonHeader(commonHeader);
479                         oamContext.setStatus(status);
480                         messageAdapter.post(oamContext);
481                     } catch(Throwable t) {
482                         status = unexpectedOAMError(t,RPC.stop);
483                     }
484
485                     LoggingUtils.auditWarn(startTime.toInstant(),
486                             new Date(System.currentTimeMillis()).toInstant(),
487                             String.valueOf(status.getCode()),
488                             status.getMessage(),
489                             this.getClass().getCanonicalName(),
490                             Msg.OAM_OPERATION_STOPPED,
491                             getAppcName()
492                     );
493                     myScheduledFuture.cancel(false);
494
495                 } finally {
496                     clearRequestLogProperties();
497                 }
498             }
499         };
500
501         MyCommand command = new MyCommand();
502
503         long initialDelay = 10000;
504         long delay = initialDelay;
505
506
507         command.myScheduledFuture = scheduledExecutorService.scheduleWithFixedDelay(
508                 command,
509                 initialDelay,
510                 delay,
511                 TimeUnit.MILLISECONDS
512         );
513         this.outstandingLCMRequestMonitorSheduledFuture = command.myScheduledFuture;
514     }
515
516
517
518
519     private void scheduleStartingAPPC(final CommonHeader commonHeader,final Date startTime){
520
521
522         class MyCommand implements Runnable{
523
524
525             @Override
526             public void run() {
527                 try {
528                     setInitialLogProperties(commonHeader, RPC.start);
529
530                     logDebug("Executing starting task ");
531
532                     Status status = buildStatus(OAMCommandStatus.SUCCESS);
533
534                     try {
535                         LCMStateManager lcmStateManager = getService(LCMStateManager.class);
536                         lcmStateManager.enableLCMOperations();
537                         //cancel the current outstandingLCMRequestMonitor
538                         outstandingLCMRequestMonitorSheduledFuture = null;
539                     } catch(Throwable t) {
540                         status = unexpectedOAMError(t,RPC.start);
541                     }
542
543                     try {
544                        OAMContext oamContext = new OAMContext();
545                        oamContext.setRpcName(RPC.start);
546                        oamContext.setCommonHeader(commonHeader);
547                        oamContext.setStatus(status);
548                        messageAdapter.post(oamContext);
549                     } catch(Throwable t) {
550                       status = unexpectedOAMError(t,RPC.start);
551                     }
552
553                     LoggingUtils.auditWarn(startTime.toInstant(),
554                             new Date(System.currentTimeMillis()).toInstant(),
555                             String.valueOf(status.getCode()),
556                             status.getMessage(),
557                             this.getClass().getCanonicalName(),
558                             Msg.OAM_OPERATION_STARTED,
559                             getAppcName()
560                     );
561                 } finally {
562                     clearRequestLogProperties();
563                 }
564             }
565         };
566
567         MyCommand command = new MyCommand();
568         long initialDelay = 1000;
569
570         scheduledExecutorService.schedule(
571                 command,
572                 initialDelay,
573                 TimeUnit.MILLISECONDS
574         );
575     }
576
577
578     private void logDebug(String message,Object... args){
579         if (logger.isDebugEnabled()) {
580             logger.debug(String.format(message,args));
581         }
582     }
583 }