cb7da95ec6b9319244dde3e6c45d5afc50d94ab5
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2021 Nordix Foundation.
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  */
21
22 package org.onap.policy.drools.lifecycle;
23
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Properties;
28 import java.util.stream.Collectors;
29 import lombok.Getter;
30 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
31 import org.onap.policy.common.endpoints.event.comm.TopicSink;
32 import org.onap.policy.common.endpoints.event.comm.TopicSource;
33 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
34 import org.onap.policy.common.utils.coder.CoderException;
35 import org.onap.policy.drools.domain.models.controller.ControllerCustomSerialization;
36 import org.onap.policy.drools.domain.models.controller.ControllerEvent;
37 import org.onap.policy.drools.domain.models.controller.ControllerPolicy;
38 import org.onap.policy.drools.domain.models.controller.ControllerProperties;
39 import org.onap.policy.drools.domain.models.controller.ControllerSinkTopic;
40 import org.onap.policy.drools.domain.models.controller.ControllerSourceTopic;
41 import org.onap.policy.drools.properties.DroolsPropertyConstants;
42 import org.onap.policy.drools.system.PolicyController;
43 import org.onap.policy.drools.system.PolicyControllerConstants;
44 import org.onap.policy.drools.system.PolicyEngineConstants;
45 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 public class PolicyTypeNativeDroolsController implements PolicyTypeController {
51     private static final Logger logger = LoggerFactory.getLogger(PolicyTypeNativeDroolsController.class);
52
53     @Getter
54     protected final ToscaConceptIdentifier policyType;
55
56     @GsonJsonIgnore
57     protected final LifecycleFsm fsm;
58
59     public PolicyTypeNativeDroolsController(LifecycleFsm fsm, ToscaConceptIdentifier policyType) {
60         this.policyType = policyType;
61         this.fsm = fsm;
62     }
63
64     @Override
65     public boolean deploy(ToscaPolicy policy) {
66         Properties controllerProps = new Properties();
67         ControllerPolicy controllerPolicy = toDomainPolicy(policy);
68         if (controllerPolicy == null) {
69             return false;
70         }
71
72         ControllerProperties controllerConfig = controllerPolicy.getProperties();
73
74         configControllerName(controllerConfig, controllerProps);
75
76         boolean success =
77             configControllerSources(controllerConfig, controllerProps)
78             && configControllerSinks(controllerConfig, controllerProps)
79             && configControllerCustom(controllerConfig, controllerProps);
80
81         if (!success) {
82             return false;
83         }
84
85         PolicyController controller;
86         try {
87             controller =
88                 PolicyEngineConstants.getManager()
89                     .createPolicyController(controllerConfig.getControllerName(), controllerProps);
90         } catch (RuntimeException e) {
91             logger.warn("failed deploy (cannot create controller) for policy: {}", policy, e);
92             return false;
93         }
94
95         try {
96             controller.start();
97         } catch (RuntimeException e) {
98             logger.warn("failed deploy (cannot start controller) for policy: {}", policy, e);
99             PolicyEngineConstants.getManager().removePolicyController(controller);
100             return false;
101         }
102
103         return true;
104     }
105
106     @Override
107     public boolean undeploy(ToscaPolicy policy) {
108         try {
109             ControllerPolicy nativePolicy = fsm.getDomainMaker().convertTo(policy, ControllerPolicy.class);
110             PolicyEngineConstants.getManager()
111                     .removePolicyController(nativePolicy.getProperties().getControllerName());
112             return true;
113         } catch (RuntimeException | CoderException e) {
114             logger.warn("failed undeploy of policy: {}", policy);
115             return false;
116         }
117     }
118
119     private ControllerPolicy toDomainPolicy(ToscaPolicy policy) {
120         ControllerPolicy nativePolicy = null;
121         try {
122             nativePolicy = fsm.getDomainMaker().convertTo(policy, ControllerPolicy.class);
123             ControllerProperties config = nativePolicy.getProperties();
124
125             /* check for duplicates */
126
127             if (isDups(sourceTopics(config.getSourceTopics()))
128                         || isDups(sinkTopics(config.getSinkTopics()))) {
129                 logger.warn("there are duplicated topics in policy {}", policy);
130                 return null;
131             }
132
133             /* check for non-existance of the controller - throws IAE if there's not */
134
135             PolicyControllerConstants.getFactory().get(nativePolicy.getProperties().getControllerName());
136
137         } catch (CoderException e) {
138             logger.warn("failed deploy of policy (invalid): {}", policy);
139             return null;
140         } catch (IllegalArgumentException e) {
141             // this is OK
142             logger.trace("proceeding with the deploy of native controller policy: {}", policy);
143         }
144
145         return nativePolicy;
146     }
147
148     private void configControllerName(ControllerProperties controllerConfig, Properties controllerProps)  {
149         controllerProps
150                 .setProperty(DroolsPropertyConstants.PROPERTY_CONTROLLER_NAME, controllerConfig.getControllerName());
151     }
152
153     private boolean configControllerSources(ControllerProperties controllerConfig, Properties controllerProps) {
154         if (controllerConfig.getSourceTopics() == null) {
155             return true;
156         }
157
158         for (ControllerSourceTopic configSourceTopic : controllerConfig.getSourceTopics()) {
159             List<TopicSource> sources =
160                     TopicEndpointManager.getManager().getTopicSources(List.of(configSourceTopic.getTopicName()));
161             if (sources.size() != 1) {
162                 logger.warn("Topic {} is not present or ambigous {}", configSourceTopic.getTopicName(), sources);
163                 return false;
164             }
165
166             configSourceTopic(sources.get(0), configSourceTopic, controllerProps);
167         }
168         return true;
169     }
170
171     private void configSourceTopic(TopicSource topic, ControllerSourceTopic configTopic, Properties controllerProps) {
172         String configCommPrefix = topic.getTopicCommInfrastructure().name().toLowerCase() + ".source";
173         configTopic(configCommPrefix, topic.getTopic(), configTopic.getEvents(), controllerProps);
174     }
175
176     private boolean configControllerSinks(ControllerProperties controllerConfig, Properties controllerProps) {
177         if (controllerConfig.getSinkTopics() == null) {
178             return true;
179         }
180
181         for (ControllerSinkTopic configSinkTopic : controllerConfig.getSinkTopics()) {
182             List<TopicSink> sinks =
183                     TopicEndpointManager.getManager().getTopicSinks(List.of(configSinkTopic.getTopicName()));
184             if (sinks.size() != 1) {
185                 logger.warn("Topic {} is not present or ambigous {}", configSinkTopic.getTopicName(), sinks);
186                 return false;
187             }
188
189             configSinkTopic(sinks.get(0), configSinkTopic, controllerProps);
190         }
191         return true;
192     }
193
194     private void configSinkTopic(TopicSink topic, ControllerSinkTopic configTopic, Properties controllerProps) {
195         String configCommPrefix = topic.getTopicCommInfrastructure().name().toLowerCase() + ".sink";
196         configTopic(configCommPrefix, topic.getTopic(), configTopic.getEvents(), controllerProps);
197     }
198
199     private void configTopic(
200             String configCommPrefix, String topicName, List<ControllerEvent> events, Properties controllerProps) {
201         String configTopicPrefix = configCommPrefix + ".topics." + topicName;
202         configTopics(configCommPrefix, topicName, controllerProps);
203         for (ControllerEvent configEvent : events) {
204             configEvent(configTopicPrefix, configEvent, controllerProps);
205         }
206     }
207
208     private void configTopics(String propPrefix, String topicName, Properties controllerProps) {
209         String topicsPropKey = propPrefix + ".topics";
210         configTopicItemList(topicsPropKey, topicName, controllerProps);
211     }
212
213     private void configEvent(String propPrefix, ControllerEvent configEvent, Properties controllerProps) {
214         String eventPropPrefix = propPrefix + ".events";
215         if (configEvent.getEventFilter() != null) {
216             controllerProps.setProperty(
217                 eventPropPrefix + "." + configEvent.getEventClass() + ".filter", configEvent.getEventFilter());
218         }
219         if (configEvent.getCustomSerialization() != null) {
220             configSerialization(eventPropPrefix, configEvent.getCustomSerialization(), controllerProps);
221         }
222         configTopicItemList(eventPropPrefix, configEvent.getEventClass(), controllerProps);
223     }
224
225     private void configTopicItemList(String itemPrefix, String item, Properties controllerProps) {
226         String itemValue = controllerProps.getProperty(itemPrefix);
227         if (itemValue == null) {
228             controllerProps.setProperty(itemPrefix, item);
229         } else {
230             controllerProps.setProperty(itemPrefix, itemValue + "," + item);
231         }
232     }
233
234     private void configSerialization(
235             String propPrefix, ControllerCustomSerialization configCustom, Properties controllerProps) {
236         String customPropPrefix = propPrefix + ".custom.gson";
237         controllerProps.setProperty(
238                 customPropPrefix, configCustom.getCustomSerializerClass() + "," + configCustom.getJsonParser());
239     }
240
241     private boolean configControllerCustom(ControllerProperties controllerConfig, Properties controllerProps) {
242         Map<String, String> configCustom = controllerConfig.getCustomConfig();
243         if (configCustom != null && !configCustom.isEmpty()) {
244             controllerProps.putAll(configCustom);
245         }
246
247         return true;
248     }
249
250     private <T> boolean isDups(List<T> items) {
251         return items.size() != items.stream().distinct().count();
252     }
253
254     private List<String> sourceTopics(List<ControllerSourceTopic> sourceTopics) {
255         if (sourceTopics == null) {
256             return Collections.emptyList();
257         }
258
259         return sourceTopics.stream()
260                        .map(ControllerSourceTopic::getTopicName)
261                        .collect(Collectors.toList());
262     }
263
264     private List<String> sinkTopics(List<ControllerSinkTopic> sinkTopics) {
265         if (sinkTopics == null) {
266             return Collections.emptyList();
267         }
268
269         return sinkTopics.stream()
270                        .map(ControllerSinkTopic::getTopicName)
271                        .collect(Collectors.toList());
272     }
273
274 }