blob: 2a30d52f0ff79391023e89ea6dcf83fbf01525b2 [file] [log] [blame]
Thomas Vachuska7a8de842016-03-07 20:56:35 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Thomas Vachuska7a8de842016-03-07 20:56:35 -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
14 * limitations under the License.
15 */
16
17package org.onosproject.cluster.impl;
18
Ray Milkeyd84f89b2018-08-17 14:54:17 -070019import org.osgi.service.component.runtime.ServiceComponentRuntime;
20import org.osgi.service.component.annotations.Activate;
21import org.osgi.service.component.annotations.Component;
22import org.osgi.service.component.annotations.Deactivate;
23import org.osgi.service.component.annotations.Reference;
24import org.osgi.service.component.annotations.ReferenceCardinality;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080025import org.apache.karaf.features.Feature;
26import org.apache.karaf.features.FeaturesService;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080027import org.onosproject.cluster.ClusterAdminService;
28import org.osgi.framework.Bundle;
29import org.osgi.framework.BundleContext;
30import org.osgi.service.component.ComponentContext;
Jordan Haltermandfc48552018-11-02 11:08:30 -070031import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070032import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080033import org.slf4j.Logger;
34import org.slf4j.LoggerFactory;
35
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080036import java.util.concurrent.Executors;
37import java.util.concurrent.ScheduledExecutorService;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080038import java.util.concurrent.ScheduledFuture;
39import java.util.concurrent.TimeUnit;
40
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080041import static org.onlab.util.Tools.groupedThreads;
42
Thomas Vachuska7a8de842016-03-07 20:56:35 -080043/**
44 * Monitors the system to make sure that all bundles and their components
45 * are properly activated and keeps the cluster node service appropriately
46 * updated.
47 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070048@Component(immediate = true)
Thomas Vachuska7a8de842016-03-07 20:56:35 -080049public class ComponentsMonitor {
50
51 private Logger log = LoggerFactory.getLogger(getClass());
52
Jordan Halterman86fdd282019-01-07 10:25:32 -080053 private static final long STARTUP_PERIOD = 2500;
54 private static final long ACTIVE_PERIOD = 60000;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080055
Ray Milkeyd84f89b2018-08-17 14:54:17 -070056 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska7a8de842016-03-07 20:56:35 -080057 protected FeaturesService featuresService;
58
Ray Milkeyd84f89b2018-08-17 14:54:17 -070059 @Reference(cardinality = ReferenceCardinality.MANDATORY)
60 protected ServiceComponentRuntime scrService;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080061
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska7a8de842016-03-07 20:56:35 -080063 protected ClusterAdminService clusterAdminService;
64
65 private BundleContext bundleContext;
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080066
67 private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(
68 groupedThreads("components-monitor", "%d", log));
Thomas Vachuska7a8de842016-03-07 20:56:35 -080069 private ScheduledFuture<?> poller;
Jordan Halterman86fdd282019-01-07 10:25:32 -080070 private boolean pollerBackedOff;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080071
72 @Activate
73 protected void activate(ComponentContext context) {
74 bundleContext = context.getBundleContext();
Jordan Halterman86fdd282019-01-07 10:25:32 -080075 poller = executor.scheduleAtFixedRate(
76 this::checkStartedState, STARTUP_PERIOD, STARTUP_PERIOD, TimeUnit.MILLISECONDS);
Thomas Vachuska7a8de842016-03-07 20:56:35 -080077 log.info("Started");
78 }
79
80 @Deactivate
81 protected void deactivate() {
82 poller.cancel(false);
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080083 executor.shutdownNow();
Thomas Vachuska7a8de842016-03-07 20:56:35 -080084 log.info("Stopped");
85 }
86
Jordan Halterman86fdd282019-01-07 10:25:32 -080087 /**
88 * Increases the rate at which {@link #checkStartedState()} is called once the node has been fully started.
89 */
90 private void backoffPoller() {
91 if (!pollerBackedOff) {
92 poller.cancel(false);
93 poller = executor.scheduleAtFixedRate(
94 this::checkStartedState, ACTIVE_PERIOD, ACTIVE_PERIOD, TimeUnit.MILLISECONDS);
95 pollerBackedOff = true;
96 }
97 }
98
99 /**
100 * Decreases the rate at which {@link #checkStartedState()} is called when the node becomes unready.
101 */
102 private void revertPoller() {
103 if (pollerBackedOff) {
104 poller.cancel(false);
105 poller = executor.scheduleAtFixedRate(
106 this::checkStartedState, STARTUP_PERIOD, STARTUP_PERIOD, TimeUnit.MILLISECONDS);
107 pollerBackedOff = false;
108 }
109 }
110
111 /**
112 * Checks whether all components are active and marks the node READY if so.
113 */
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800114 private void checkStartedState() {
Jordan Halterman86fdd282019-01-07 10:25:32 -0800115 boolean isFullyStarted = isFullyStarted();
116 clusterAdminService.markFullyStarted(isFullyStarted);
117
118 // If the node is fully started, decrease the rate at which we poll component states.
119 // Otherwise, increase the rate at which we poll component states until the node becomes ready.
120 if (isFullyStarted) {
121 backoffPoller();
122 } else {
123 revertPoller();
124 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800125 }
126
127 /**
128 * Scans the system to make sure that all bundles and their components
129 * are fully started.
130 *
131 * @return true if all bundles and their components are active
132 */
133 private boolean isFullyStarted() {
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700134 try {
135 for (Feature feature : featuresService.listInstalledFeatures()) {
136 if (!isFullyStarted(feature)) {
137 return false;
138 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800139 }
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 return true;
141 } catch (Exception ex) {
142 return false;
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800143 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800144 }
145
146 private boolean isFullyStarted(Feature feature) {
Ray Milkeyf13feb82016-08-30 10:38:59 -0700147 try {
148 return feature.getBundles().stream()
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700149 .map(info -> bundleContext.getBundle())
Ray Milkeyf13feb82016-08-30 10:38:59 -0700150 .allMatch(this::isFullyStarted);
151 } catch (NullPointerException npe) {
152 // FIXME: Remove this catch block when Felix fixes the bug
153 // Due to a bug in the Felix implementation, this can throw an NPE.
154 // Catch the error and do something sensible with it.
155 return false;
156 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800157 }
158
159 private boolean isFullyStarted(Bundle bundle) {
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700160 for (ComponentDescriptionDTO component : scrService.getComponentDescriptionDTOs(bundle)) {
Jordan Haltermandfc48552018-11-02 11:08:30 -0700161 if (scrService.isComponentEnabled(component)) {
162 for (ComponentConfigurationDTO config : scrService.getComponentConfigurationDTOs(component)) {
163 if (config.state != ComponentConfigurationDTO.ACTIVE) {
164 return false;
165 }
166 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800167 }
168 }
169 return true;
170 }
171
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800172}