blob: 551c646a2b656a98a2111642223de5dc28cd3119 [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
Charles Chan5bdaf102020-08-10 16:34:32 -070019import com.google.common.collect.Lists;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070020import org.osgi.service.component.runtime.ServiceComponentRuntime;
21import org.osgi.service.component.annotations.Activate;
22import org.osgi.service.component.annotations.Component;
23import org.osgi.service.component.annotations.Deactivate;
24import org.osgi.service.component.annotations.Reference;
25import org.osgi.service.component.annotations.ReferenceCardinality;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080026import org.apache.karaf.features.Feature;
27import org.apache.karaf.features.FeaturesService;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080028import org.onosproject.cluster.ClusterAdminService;
Charles Chan5bdaf102020-08-10 16:34:32 -070029import org.onosproject.cluster.ComponentsMonitorService;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080030import org.osgi.framework.Bundle;
31import org.osgi.framework.BundleContext;
32import org.osgi.service.component.ComponentContext;
Jordan Haltermandfc48552018-11-02 11:08:30 -070033import org.osgi.service.component.runtime.dto.ComponentConfigurationDTO;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070034import org.osgi.service.component.runtime.dto.ComponentDescriptionDTO;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080035import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
Charles Chan5bdaf102020-08-10 16:34:32 -070038import java.util.List;
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080039import java.util.concurrent.Executors;
40import java.util.concurrent.ScheduledExecutorService;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080041import java.util.concurrent.ScheduledFuture;
42import java.util.concurrent.TimeUnit;
43
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080044import static org.onlab.util.Tools.groupedThreads;
45
Thomas Vachuska7a8de842016-03-07 20:56:35 -080046/**
47 * Monitors the system to make sure that all bundles and their components
48 * are properly activated and keeps the cluster node service appropriately
49 * updated.
50 */
Charles Chan5bdaf102020-08-10 16:34:32 -070051@Component(immediate = true, service = { ComponentsMonitorService.class })
52public class ComponentsMonitorManager implements ComponentsMonitorService {
Thomas Vachuska7a8de842016-03-07 20:56:35 -080053
54 private Logger log = LoggerFactory.getLogger(getClass());
55
Jordan Halterman86fdd282019-01-07 10:25:32 -080056 private static final long STARTUP_PERIOD = 2500;
57 private static final long ACTIVE_PERIOD = 60000;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080058
Ray Milkeyd84f89b2018-08-17 14:54:17 -070059 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska7a8de842016-03-07 20:56:35 -080060 protected FeaturesService featuresService;
61
Ray Milkeyd84f89b2018-08-17 14:54:17 -070062 @Reference(cardinality = ReferenceCardinality.MANDATORY)
63 protected ServiceComponentRuntime scrService;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080064
Ray Milkeyd84f89b2018-08-17 14:54:17 -070065 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Thomas Vachuska7a8de842016-03-07 20:56:35 -080066 protected ClusterAdminService clusterAdminService;
67
68 private BundleContext bundleContext;
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080069
70 private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(
71 groupedThreads("components-monitor", "%d", log));
Thomas Vachuska7a8de842016-03-07 20:56:35 -080072 private ScheduledFuture<?> poller;
Jordan Halterman86fdd282019-01-07 10:25:32 -080073 private boolean pollerBackedOff;
Thomas Vachuska7a8de842016-03-07 20:56:35 -080074
75 @Activate
76 protected void activate(ComponentContext context) {
77 bundleContext = context.getBundleContext();
Jordan Halterman86fdd282019-01-07 10:25:32 -080078 poller = executor.scheduleAtFixedRate(
79 this::checkStartedState, STARTUP_PERIOD, STARTUP_PERIOD, TimeUnit.MILLISECONDS);
Thomas Vachuska7a8de842016-03-07 20:56:35 -080080 log.info("Started");
81 }
82
83 @Deactivate
84 protected void deactivate() {
85 poller.cancel(false);
Jordan Haltermanaf3461c2019-01-07 10:03:46 -080086 executor.shutdownNow();
Thomas Vachuska7a8de842016-03-07 20:56:35 -080087 log.info("Stopped");
88 }
89
Jordan Halterman86fdd282019-01-07 10:25:32 -080090 /**
91 * Increases the rate at which {@link #checkStartedState()} is called once the node has been fully started.
92 */
93 private void backoffPoller() {
94 if (!pollerBackedOff) {
95 poller.cancel(false);
96 poller = executor.scheduleAtFixedRate(
97 this::checkStartedState, ACTIVE_PERIOD, ACTIVE_PERIOD, TimeUnit.MILLISECONDS);
98 pollerBackedOff = true;
99 }
100 }
101
102 /**
103 * Decreases the rate at which {@link #checkStartedState()} is called when the node becomes unready.
104 */
105 private void revertPoller() {
106 if (pollerBackedOff) {
107 poller.cancel(false);
108 poller = executor.scheduleAtFixedRate(
109 this::checkStartedState, STARTUP_PERIOD, STARTUP_PERIOD, TimeUnit.MILLISECONDS);
110 pollerBackedOff = false;
111 }
112 }
113
114 /**
115 * Checks whether all components are active and marks the node READY if so.
116 */
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800117 private void checkStartedState() {
Jordan Halterman86fdd282019-01-07 10:25:32 -0800118 boolean isFullyStarted = isFullyStarted();
119 clusterAdminService.markFullyStarted(isFullyStarted);
120
121 // If the node is fully started, decrease the rate at which we poll component states.
122 // Otherwise, increase the rate at which we poll component states until the node becomes ready.
123 if (isFullyStarted) {
124 backoffPoller();
125 } else {
126 revertPoller();
127 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800128 }
129
130 /**
131 * Scans the system to make sure that all bundles and their components
132 * are fully started.
133 *
134 * @return true if all bundles and their components are active
135 */
136 private boolean isFullyStarted() {
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700137 try {
138 for (Feature feature : featuresService.listInstalledFeatures()) {
Charles Chan5bdaf102020-08-10 16:34:32 -0700139 if (needToCheck(feature) && !isFullyStarted(feature)) {
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700140 return false;
141 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800142 }
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700143 return true;
144 } catch (Exception ex) {
145 return false;
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800146 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800147 }
148
Ray Milkey9ee53ef2019-01-07 13:02:38 -0800149 private boolean needToCheck(Feature feature) {
150 // We only need to check core ONOS features, not external ones.
151 return feature.getId().startsWith("onos-") &&
152 !feature.getId().contains("thirdparty");
153 }
154
Charles Chan5bdaf102020-08-10 16:34:32 -0700155 @Override
156 public boolean isFullyStarted(List<String> featureStrings) {
157 List<Feature> features = Lists.newArrayList();
158 for (String featureString : featureStrings) {
Ray Milkey9ee53ef2019-01-07 13:02:38 -0800159 try {
Charles Chan5bdaf102020-08-10 16:34:32 -0700160 features.add(featuresService.getFeature(featureString));
161 } catch (Exception e) {
162 log.debug("Feature {} not found", featureString);
Ray Milkey9ee53ef2019-01-07 13:02:38 -0800163 return false;
164 }
Charles Chan5bdaf102020-08-10 16:34:32 -0700165 }
166 return features.stream().allMatch(this::isFullyStarted);
167 }
168
169 private boolean isFullyStarted(Feature feature) {
170 try {
171 return feature.getBundles().stream()
172 .map(info -> bundleContext.getBundle(info.getLocation()))
173 .allMatch(bundle -> bundle != null && isFullyStarted(bundle));
174 } catch (NullPointerException npe) {
175 // FIXME: Remove this catch block when Felix fixes the bug
176 // Due to a bug in the Felix implementation, this can throw an NPE.
177 // Catch the error and do something sensible with it.
178 return false;
Ray Milkeyf13feb82016-08-30 10:38:59 -0700179 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800180 }
181
182 private boolean isFullyStarted(Bundle bundle) {
Ray Milkeyd84f89b2018-08-17 14:54:17 -0700183 for (ComponentDescriptionDTO component : scrService.getComponentDescriptionDTOs(bundle)) {
Jordan Haltermandfc48552018-11-02 11:08:30 -0700184 if (scrService.isComponentEnabled(component)) {
185 for (ComponentConfigurationDTO config : scrService.getComponentConfigurationDTOs(component)) {
186 if (config.state != ComponentConfigurationDTO.ACTIVE) {
187 return false;
188 }
189 }
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800190 }
191 }
192 return true;
193 }
194
Thomas Vachuska7a8de842016-03-07 20:56:35 -0800195}