blob: 8eb7c0c029494dae2aa5e304b8c95a0bffacd309 [file] [log] [blame]
jaegonkim6a7b5242018-09-12 23:09:42 +09001/*
2 * Copyright 2018-present Open Networking Foundation
3 *
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 */
16package org.onosproject.workflow.impl;
17
mohamedrahilr63a921c2019-02-27 19:48:25 +053018
jaegonkim6a7b5242018-09-12 23:09:42 +090019import com.fasterxml.jackson.databind.JsonNode;
20import com.fasterxml.jackson.databind.node.JsonNodeFactory;
mohamedrahilr63a921c2019-02-27 19:48:25 +053021import com.fasterxml.jackson.databind.node.JsonNodeType;
jaegonkim6a7b5242018-09-12 23:09:42 +090022import org.onosproject.net.config.NetworkConfigRegistry;
23import org.onosproject.net.config.NetworkConfigService;
jaegonkim6a7b5242018-09-12 23:09:42 +090024import org.onosproject.workflow.api.WorkflowService;
25import org.onosproject.workflow.api.WorkflowExecutionService;
jaegonkim6a7b5242018-09-12 23:09:42 +090026import org.onosproject.workflow.api.WorkplaceStore;
mohamedrahilr63a921c2019-02-27 19:48:25 +053027import org.onosproject.workflow.api.WorkflowStore;
28import org.onosproject.workflow.api.WorkplaceDescription;
29import org.onosproject.workflow.api.WorkflowException;
30import org.onosproject.workflow.api.DefaultWorkplace;
31import org.onosproject.workflow.api.JsonDataModelTree;
32import org.onosproject.workflow.api.WorkflowDescription;
33import org.onosproject.workflow.api.Workplace;
34import org.onosproject.workflow.api.WorkflowDataModelException;
35import org.onosproject.workflow.api.Workflow;
36import org.onosproject.workflow.api.Worklet;
37import org.onosproject.workflow.api.WorkflowContext;
38import org.onosproject.workflow.api.JsonDataModel;
Ray Milkeydf521292018-10-04 15:13:33 -070039import org.osgi.service.component.annotations.Activate;
40import org.osgi.service.component.annotations.Component;
41import org.osgi.service.component.annotations.Deactivate;
42import org.osgi.service.component.annotations.Reference;
43import org.osgi.service.component.annotations.ReferenceCardinality;
jaegonkim6a7b5242018-09-12 23:09:42 +090044import org.slf4j.Logger;
45
mohamedrahilr63a921c2019-02-27 19:48:25 +053046import java.lang.annotation.Annotation;
47import java.lang.reflect.Field;
jaegonkim6a7b5242018-09-12 23:09:42 +090048import java.net.URI;
mohamedrahilr63a921c2019-02-27 19:48:25 +053049import java.util.Map;
50import java.util.HashMap;
jaegonkim6a7b5242018-09-12 23:09:42 +090051import java.util.Objects;
mohamedrahilr63a921c2019-02-27 19:48:25 +053052import java.util.Arrays;
53import java.util.regex.Matcher;
54import java.util.regex.Pattern;
jaegonkim6a7b5242018-09-12 23:09:42 +090055
56import static org.slf4j.LoggerFactory.getLogger;
57
Ray Milkeydf521292018-10-04 15:13:33 -070058@Component(immediate = true, service = WorkflowService.class)
jaegonkim6a7b5242018-09-12 23:09:42 +090059public class WorkflowManager implements WorkflowService {
60
61 protected static final Logger log = getLogger(WorkflowManager.class);
62
Ray Milkeydf521292018-10-04 15:13:33 -070063 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim6a7b5242018-09-12 23:09:42 +090064 private WorkflowExecutionService workflowExecutionService;
65
Ray Milkeydf521292018-10-04 15:13:33 -070066 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim6a7b5242018-09-12 23:09:42 +090067 protected WorkplaceStore workplaceStore;
68
Ray Milkeydf521292018-10-04 15:13:33 -070069 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim6a7b5242018-09-12 23:09:42 +090070 protected WorkflowStore workflowStore;
71
Ray Milkeydf521292018-10-04 15:13:33 -070072 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim6a7b5242018-09-12 23:09:42 +090073 private NetworkConfigService networkConfigService;
74
Ray Milkeydf521292018-10-04 15:13:33 -070075 @Reference(cardinality = ReferenceCardinality.MANDATORY)
jaegonkim6a7b5242018-09-12 23:09:42 +090076 private NetworkConfigRegistry networkConfigRegistry;
77
78 private WorkflowNetConfigListener netcfgListener;
79
80 @Activate
81 public void activate() {
82 netcfgListener = new WorkflowNetConfigListener(this);
83 networkConfigRegistry.registerConfigFactory(netcfgListener.getConfigFactory());
84 networkConfigService.addListener(netcfgListener);
85 log.info("Started");
86 }
87
88 @Deactivate
89 public void deactivate() {
90 networkConfigService.removeListener(netcfgListener);
91 networkConfigRegistry.unregisterConfigFactory(netcfgListener.getConfigFactory());
92 log.info("Stopped");
93 }
94
95 @Override
96 public void createWorkplace(WorkplaceDescription wpDesc) throws WorkflowException {
97 log.info("createWorkplace: {}", wpDesc);
98
99 JsonNode root;
100 if (wpDesc.data().isPresent()) {
101 root = wpDesc.data().get();
102 } else {
103 root = JsonNodeFactory.instance.objectNode();
104 }
105 DefaultWorkplace workplace =
106 new DefaultWorkplace(wpDesc.name(), new JsonDataModelTree(root));
107 workplaceStore.registerWorkplace(wpDesc.name(), workplace);
108 }
109
110 @Override
111 public void removeWorkplace(WorkplaceDescription wpDesc) throws WorkflowException {
112 log.info("removeWorkplace: {}", wpDesc);
113 //TODO: Removing workflows belong to this workplace
114 workplaceStore.removeWorkplace(wpDesc.name());
115 }
116
117 @Override
118 public void clearWorkplace() throws WorkflowException {
119 log.info("clearWorkplace");
120 workplaceStore.getWorkplaces().stream()
121 .filter(wp -> !Objects.equals(wp.name(), Workplace.SYSTEM_WORKPLACE))
122 .forEach(wp -> workplaceStore.removeWorkplace(wp.name()));
123 }
124
125 @Override
126 public void invokeWorkflow(WorkflowDescription wfDesc) throws WorkflowException {
127 invokeWorkflow(wfDesc.toJson());
128 }
129
130 @Override
131 public void invokeWorkflow(JsonNode worklowDescJson) throws WorkflowException {
132 log.info("invokeWorkflow: {}", worklowDescJson);
jaegonkim6a7b5242018-09-12 23:09:42 +0900133 Workplace workplace = workplaceStore.getWorkplace(Workplace.SYSTEM_WORKPLACE);
134 if (Objects.isNull(workplace)) {
135 throw new WorkflowException("Invalid system workplace");
136 }
137
mohamedrahilr63a921c2019-02-27 19:48:25 +0530138 Workflow workflow = workflowStore.get(URI.create(worklowDescJson.get("id").asText()));
jaegonkim6a7b5242018-09-12 23:09:42 +0900139 if (Objects.isNull(workflow)) {
mohamedrahilr63a921c2019-02-27 19:48:25 +0530140 throw new WorkflowException("Invalid Workflow");
141 }
142
143 if (!checkWorkflowSchema(workflow, worklowDescJson)) {
144 throw new WorkflowException("Invalid Workflow " + worklowDescJson.get("id").asText());
145 }
146
147 Workflow wfCreationWf = workflowStore.get(URI.create(WorkplaceWorkflow.WF_CREATE_WORKFLOW));
148 if (Objects.isNull(wfCreationWf)) {
jaegonkim6a7b5242018-09-12 23:09:42 +0900149 throw new WorkflowException("Invalid workflow " + WorkplaceWorkflow.WF_CREATE_WORKFLOW);
150 }
151
mohamedrahilr63a921c2019-02-27 19:48:25 +0530152 WorkflowContext context = wfCreationWf.buildSystemContext(workplace, new JsonDataModelTree(worklowDescJson));
jaegonkim6a7b5242018-09-12 23:09:42 +0900153 workflowExecutionService.execInitWorklet(context);
154 }
155
mohamedrahilr63a921c2019-02-27 19:48:25 +0530156 /**
157 * Checks if the type of worklet is same as that of wfdesc Json.
158 *
159 * @param workflow workflow
160 * @param jsonNode jsonNode
161 * @throws WorkflowException workflow exception
162 */
163
164 private boolean checkWorkflowSchema(Workflow workflow, JsonNode jsonNode) throws WorkflowException {
165
166 Map<String, Map<String, String>> workletDataTypeMap = new HashMap<>();
167 for (String workletType : workflow.getWorkletTypeList()) {
168 Map<String, String> jsonDataModelMap = new HashMap<>();
169 if (Objects.equals(workletType, Worklet.Common.INIT.tag())
170 || (Objects.equals(workletType, Worklet.Common.COMPLETED.tag()))) {
171 continue;
172 }
173 Worklet worklet = workflow.getWorkletInstance(workletType);
174 Class cls = worklet.getClass();
175 for (Field field : cls.getDeclaredFields()) {
176 if (field.isSynthetic()) {
177 continue;
178 }
179 Annotation[] annotations = field.getAnnotations();
180 for (Annotation annotation : annotations) {
181 if (annotation instanceof JsonDataModel) {
182 JsonDataModel jsonDataModel = (JsonDataModel) annotation;
183 Matcher matcher = Pattern.compile("(\\w+)").matcher(jsonDataModel.path());
184 if (!matcher.find()) {
185 throw new WorkflowException("Invalid Json Data Model Path");
186 }
187 String path = matcher.group(1);
188 if (checkJsonNodeDataType(jsonNode, field, path)) {
189 jsonDataModelMap.put(path, field.getType().getName());
190 }
191 }
192 }
193 }
194 if (!jsonDataModelMap.isEmpty()) {
195 workletDataTypeMap.put(worklet.tag(), jsonDataModelMap);
196 }
197
198 }
199 if (!workletDataTypeMap.isEmpty()) {
200 throw new WorkflowDataModelException("invalid workflow ", workflow.id().toString(), workletDataTypeMap);
201 }
202 return true;
203 }
204
205
206 private boolean checkJsonNodeDataType(JsonNode jsonNode, Field field, String path) throws WorkflowException {
207 if (!Objects.nonNull(jsonNode.get("data")) && !Objects.nonNull(jsonNode.get("data").get(path))) {
208 throw new WorkflowException("Invalid Json");
209 }
210 JsonNodeType jsonNodeType = jsonNode.get("data").get(path).getNodeType();
211 if (jsonNodeType != null) {
212 switch (jsonNodeType) {
213 case NUMBER:
214 if (!(field.getType().isAssignableFrom(Integer.class))) {
215 return true;
216 }
217 break;
218 case STRING:
219 if (!(field.getType().isAssignableFrom(String.class))) {
220 return true;
221 }
222 break;
223 case OBJECT:
224 if (!(field.getType().isAssignableFrom(Objects.class))) {
225 return true;
226 }
227 break;
228 case BOOLEAN:
229 if (!(field.getType().isAssignableFrom(Boolean.class))) {
230 return true;
231 }
232 break;
233 case ARRAY:
234 if (!(field.getType().isAssignableFrom(Arrays.class))) {
235 return true;
236 }
237 break;
238 default:
239 return true;
240 }
241 } else {
242 return false;
243
244 }
245 return false;
246 }
247
jaegonkim6a7b5242018-09-12 23:09:42 +0900248 @Override
249 public void terminateWorkflow(WorkflowDescription wfDesc) throws WorkflowException {
250 log.info("terminateWorkflow: {}", wfDesc);
251 if (Objects.nonNull(workplaceStore.getContext(wfDesc.workflowContextName()))) {
252 workplaceStore.removeContext(wfDesc.workflowContextName());
253 }
254 }
255}