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