push addional code
[sdc.git] / openecomp-be / lib / openecomp-sdc-translator-lib / openecomp-sdc-translator-core / src / main / java / org / openecomp / sdc / translator / services / heattotosca / impl / ResourceTranslationNovaServerImpl.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 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.openecomp.sdc.translator.services.heattotosca.impl;
22
23 import org.apache.commons.collections4.CollectionUtils;
24 import org.apache.commons.collections4.MapUtils;
25 import org.openecomp.sdc.heat.datatypes.model.HeatOrchestrationTemplate;
26 import org.openecomp.sdc.heat.datatypes.model.HeatResourcesTypes;
27 import org.openecomp.sdc.heat.datatypes.model.Resource;
28 import org.openecomp.sdc.tosca.datatypes.ToscaCapabilityType;
29 import org.openecomp.sdc.tosca.datatypes.ToscaNodeType;
30 import org.openecomp.sdc.tosca.datatypes.ToscaRelationshipType;
31 import org.openecomp.sdc.tosca.datatypes.model.NodeTemplate;
32 import org.openecomp.sdc.tosca.datatypes.model.NodeType;
33 import org.openecomp.sdc.tosca.datatypes.model.RelationshipTemplate;
34 import org.openecomp.sdc.tosca.datatypes.model.RequirementAssignment;
35 import org.openecomp.sdc.tosca.datatypes.model.ServiceTemplate;
36 import org.openecomp.sdc.tosca.services.DataModelUtil;
37 import org.openecomp.sdc.tosca.services.ToscaConstants;
38 import org.openecomp.sdc.translator.datatypes.heattotosca.AttachedResourceId;
39 import org.openecomp.sdc.translator.datatypes.heattotosca.to.TranslateTo;
40 import org.openecomp.sdc.translator.datatypes.heattotosca.to.TranslatedHeatResource;
41 import org.openecomp.sdc.translator.services.heattotosca.Constants;
42 import org.openecomp.sdc.translator.services.heattotosca.HeatToToscaUtil;
43 import org.openecomp.sdc.translator.services.heattotosca.ResourceTranslationFactory;
44 import org.openecomp.sdc.translator.services.heattotosca.TranslationContext;
45 import org.openecomp.sdc.translator.services.heattotosca.helper.NameExtractorService;
46 import org.openecomp.sdc.translator.services.heattotosca.helper.PropertyRegexMatcher;
47 import org.openecomp.sdc.translator.services.heattotosca.helper.impl.NameExtractorServiceImpl;
48 import org.openecomp.sdc.translator.services.heattotosca.mapping.TranslatorHeatToToscaPropertyConverter;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.Objects;
59 import java.util.Optional;
60
61 public class ResourceTranslationNovaServerImpl extends ResourceTranslationBase {
62   protected static Logger logger = LoggerFactory.getLogger(ResourceTranslationNovaServerImpl.class);
63
64   @Override
65   protected void translate(TranslateTo translateTo) {
66     TranslationContext context = translateTo.getContext();
67     Map<String, Object> properties = translateTo.getResource().getProperties();
68     String heatFileName = translateTo.getHeatFileName();
69
70     ServiceTemplate serviceTemplate = translateTo.getServiceTemplate();
71
72     String nodeTypeRef =
73         createLocalNodeType(serviceTemplate, translateTo.getResource().getProperties(),
74             translateTo.getTranslatedId());
75
76     NodeTemplate novaNodeTemplate = new NodeTemplate();
77     novaNodeTemplate.setType(nodeTypeRef);
78     HeatOrchestrationTemplate heatOrchestrationTemplate =
79         translateTo.getHeatOrchestrationTemplate();
80     novaNodeTemplate.setProperties(TranslatorHeatToToscaPropertyConverter
81         .getToscaPropertiesSimpleConversion(properties, novaNodeTemplate.getProperties(),
82             heatFileName, heatOrchestrationTemplate, translateTo.getResource().getType(),
83             novaNodeTemplate, context));
84
85     manageNovaServerNetwork(heatFileName, serviceTemplate, heatOrchestrationTemplate,
86         translateTo.getResource(), translateTo.getTranslatedId(), context, novaNodeTemplate);
87     manageNovaServerBlockDeviceMapping(heatFileName, serviceTemplate, novaNodeTemplate,
88         heatOrchestrationTemplate, translateTo.getResource(), translateTo.getResourceId(),
89         translateTo.getTranslatedId(), context);
90
91     manageNovaServerGroupMapping(translateTo, context, properties, heatFileName, serviceTemplate,
92         heatOrchestrationTemplate);
93     DataModelUtil.addNodeTemplate(serviceTemplate, translateTo.getTranslatedId(), novaNodeTemplate);
94   }
95
96   private void manageNovaServerGroupMapping(TranslateTo translateTo, TranslationContext context,
97                                             Map<String, Object> properties, String heatFileName,
98                                             ServiceTemplate serviceTemplate,
99                                             HeatOrchestrationTemplate heatOrchestrationTemplate) {
100     if (isSchedulerHintsPropExist(properties)) {
101       Object schedulerHints = properties.get("scheduler_hints");
102       if (schedulerHints instanceof Map) {
103         addServerGroupHintsToPoliciesProups(translateTo, context, heatFileName, serviceTemplate,
104             heatOrchestrationTemplate, (Map<String, Object>) schedulerHints);
105       } else {
106         logger.warn("'scheduler_hints' property of resource '" + translateTo.getResourceId()
107             + "' is not valid. This property should be a map");
108       }
109     }
110   }
111
112   private void addServerGroupHintsToPoliciesProups(TranslateTo translateTo,
113                                                    TranslationContext context, String heatFileName,
114                                                    ServiceTemplate serviceTemplate,
115                                                 HeatOrchestrationTemplate heatOrchestrationTemplate,
116                                                 Map<String, Object> schedulerHints) {
117     for (Object hint : schedulerHints.values()) {
118       Optional<AttachedResourceId> attachedResourceId = HeatToToscaUtil
119           .extractAttachedResourceId(heatFileName, heatOrchestrationTemplate, context, hint);
120       if (attachedResourceId.isPresent()) {
121         AttachedResourceId serverGroupResourceId = attachedResourceId.get();
122         Object serverGroupResourceToTranslate = serverGroupResourceId.getEntityId();
123         if (serverGroupResourceId.isGetResource()) {
124           boolean isHintOfTypeNovaServerGroup =
125               isHintOfTypeNovaServerGroup(heatOrchestrationTemplate,
126                   serverGroupResourceToTranslate);
127           if (isHintOfTypeNovaServerGroup) {
128             addNovaServerToPolicyGroup(translateTo, context, heatFileName, serviceTemplate,
129                 heatOrchestrationTemplate, (String) serverGroupResourceToTranslate);
130           }
131         } else if (serverGroupResourceId.isGetParam()) {
132           TranslatedHeatResource translatedServerGroupResource =
133               context.getHeatSharedResourcesByParam().get(serverGroupResourceToTranslate);
134           if (Objects.nonNull(translatedServerGroupResource)
135               && !HeatToToscaUtil.isHeatFileNested(translateTo, translateTo.getHeatFileName())) {
136             serviceTemplate.getTopology_template().getGroups()
137                 .get(translatedServerGroupResource.getTranslatedId()).getMembers()
138                 .add(translateTo.getTranslatedId());
139           }
140         }
141       }
142     }
143   }
144
145   private boolean isHintOfTypeNovaServerGroup(HeatOrchestrationTemplate heatOrchestrationTemplate,
146                                               Object resourceToTranslate) {
147     return heatOrchestrationTemplate.getResources().get(resourceToTranslate).getType()
148         .equals(HeatResourcesTypes.NOVA_SERVER_GROUP_RESOURCE_TYPE.getHeatResource());
149   }
150
151   private void addNovaServerToPolicyGroup(TranslateTo translateTo, TranslationContext context,
152                                           String heatFileName, ServiceTemplate serviceTemplate,
153                                           HeatOrchestrationTemplate heatOrchestrationTemplate,
154                                           String resourceToTranslate) {
155     Resource serverGroup =
156         HeatToToscaUtil.getResource(heatOrchestrationTemplate, resourceToTranslate, heatFileName);
157     Optional<String> serverGroupTranslatedId = ResourceTranslationFactory.getInstance(serverGroup)
158         .translateResource(heatFileName, serviceTemplate, heatOrchestrationTemplate, serverGroup,
159             resourceToTranslate, context);
160     if (serverGroupTranslatedId.isPresent()) {
161       serviceTemplate.getTopology_template().getGroups().get(serverGroupTranslatedId.get())
162           .getMembers().add(translateTo.getTranslatedId());
163     }
164   }
165
166   private boolean isSchedulerHintsPropExist(Map<String, Object> properties) {
167     return !MapUtils.isEmpty(properties) && Objects.nonNull(properties.get("scheduler_hints"));
168   }
169
170   private void manageNovaServerBlockDeviceMapping(String heatFileName,
171                                                   ServiceTemplate serviceTemplate,
172                                                   NodeTemplate novaNodeTemplate,
173                                                 HeatOrchestrationTemplate heatOrchestrationTemplate,
174                                                 Resource resource, String resourceId,
175                                                 String novaServerTranslatedId,
176                                                 TranslationContext context) {
177
178     List<Map<String, Object>> blockDeviceMappingList = getBlockDeviceMappingList(resource);
179     if (CollectionUtils.isEmpty(blockDeviceMappingList)) {
180       return;
181     }
182
183     Object volumeIdObject;
184     Object snapshotIdObject;
185     String volumeResourceId;
186     int index = 0;
187     for (Map<String, Object> blockDeviceMapping : blockDeviceMappingList) {
188       volumeIdObject = blockDeviceMapping.get("volume_id");
189       snapshotIdObject = blockDeviceMapping.get("snapshot_id");
190
191       if (volumeIdObject == null && snapshotIdObject == null) {
192         logger.warn("Resource '" + resourceId
193             + "' has block_device_mapping property with empty/missing volume_id and snapshot_id "
194             + "properties. Entry number "
195             + (index + 1) + ", this entry will be ignored in TOSCA translation.");
196         index++;
197         continue;
198       }
199       if (volumeIdObject == null) {
200         String deviceName = (String) blockDeviceMapping.get("device_name");
201         String relationshipId = novaServerTranslatedId + "_" + index;
202
203         Optional<AttachedResourceId> attachedSnapshotId = HeatToToscaUtil
204             .extractAttachedResourceId(heatFileName, heatOrchestrationTemplate, context,
205                 snapshotIdObject);
206         volumeResourceId = novaServerTranslatedId + "_" + attachedSnapshotId.get().getEntityId();
207         createVolumeAttachesToRelationship(serviceTemplate, deviceName, novaServerTranslatedId,
208             volumeResourceId, relationshipId);
209         createCinderVolumeNodeTemplate(serviceTemplate, volumeResourceId, heatFileName,
210             blockDeviceMapping, heatOrchestrationTemplate, context);
211         connectNovaServerToVolume(novaNodeTemplate, volumeResourceId, relationshipId);
212       } else {
213         Optional<AttachedResourceId> attachedVolumeId = HeatToToscaUtil
214             .extractAttachedResourceId(heatFileName, heatOrchestrationTemplate, context,
215                 volumeIdObject);
216         if (attachedVolumeId.get().isGetResource()) {
217           connectNovaServerToVolume(novaNodeTemplate,
218               (String) attachedVolumeId.get().getTranslatedId(), null);
219         }
220       }
221       index++;
222     }
223   }
224
225   private void connectNovaServerToVolume(NodeTemplate novaNodeTemplate, String volumeResourceId,
226                                          String relationshipId) {
227     RequirementAssignment requirementAssignment = new RequirementAssignment();
228     requirementAssignment.setCapability(ToscaCapabilityType.ATTACHMENT.getDisplayName());
229     requirementAssignment.setNode(volumeResourceId);
230     if (relationshipId != null) {
231       requirementAssignment.setRelationship(relationshipId);
232     } else {
233       requirementAssignment
234           .setRelationship(ToscaRelationshipType.NATIVE_ATTACHES_TO.getDisplayName());
235     }
236     DataModelUtil
237         .addRequirementAssignment(novaNodeTemplate, ToscaConstants.LOCAL_STORAGE_REQUIREMENT_ID,
238             requirementAssignment);
239   }
240
241   private void createCinderVolumeNodeTemplate(ServiceTemplate serviceTemplate,
242                                               String volumeResourceId, String heatFileName,
243                                               Map<String, Object> blockDeviceMapping,
244                                               HeatOrchestrationTemplate heatOrchestrationTemplate,
245                                               TranslationContext context) {
246     NodeTemplate cinderVolumeNodeTemplate = new NodeTemplate();
247     cinderVolumeNodeTemplate.setType(ToscaNodeType.CINDER_VOLUME.getDisplayName());
248     cinderVolumeNodeTemplate.setProperties(TranslatorHeatToToscaPropertyConverter
249         .getToscaPropertiesSimpleConversion(blockDeviceMapping, null, heatFileName,
250             heatOrchestrationTemplate,
251             HeatResourcesTypes.CINDER_VOLUME_RESOURCE_TYPE.getHeatResource(),
252             cinderVolumeNodeTemplate, context));
253     DataModelUtil.addNodeTemplate(serviceTemplate, volumeResourceId, cinderVolumeNodeTemplate);
254   }
255
256   private void createVolumeAttachesToRelationship(ServiceTemplate serviceTemplate,
257                                                   String deviceName, String novaServerTranslatedId,
258                                                   String volumeId, String relationshipId) {
259     RelationshipTemplate relationshipTemplate = new RelationshipTemplate();
260     relationshipTemplate.setType(ToscaRelationshipType.CINDER_VOLUME_ATTACHES_TO.getDisplayName());
261     Map<String, Object> properties = new HashMap<>();
262     properties.put("instance_uuid", novaServerTranslatedId);
263     properties.put("volume_id", volumeId);
264     if (deviceName != null) {
265       properties.put("device", deviceName);
266     }
267     relationshipTemplate.setProperties(properties);
268
269     DataModelUtil.addRelationshipTemplate(serviceTemplate, relationshipId, relationshipTemplate);
270   }
271
272   private List<Map<String, Object>> getBlockDeviceMappingList(Resource resource) {
273
274     if (Objects.isNull(resource.getProperties())) {
275       return Collections.emptyList();
276     }
277     List<Map<String, Object>> blockDeviceMappingList =
278         (List<Map<String, Object>>) resource.getProperties().get("block_device_mapping");
279     List<Map<String, Object>> blockDeviceMappingV2List =
280         (List<Map<String, Object>>) resource.getProperties().get("block_device_mapping_v2");
281
282     if (blockDeviceMappingList != null && blockDeviceMappingV2List != null) {
283       blockDeviceMappingList.addAll(blockDeviceMappingV2List);
284     } else if (CollectionUtils.isEmpty(blockDeviceMappingList)
285         && CollectionUtils.isEmpty(blockDeviceMappingV2List)) {
286       return null;
287
288     } else {
289       blockDeviceMappingList =
290           blockDeviceMappingList != null ? blockDeviceMappingList : blockDeviceMappingV2List;
291     }
292     return blockDeviceMappingList;
293   }
294
295   private void manageNovaServerNetwork(String heatFileName, ServiceTemplate serviceTemplate,
296                                        HeatOrchestrationTemplate heatOrchestrationTemplate,
297                                        Resource resource, String translatedId,
298                                        TranslationContext context, NodeTemplate novaNodeTemplate) {
299
300     if (resource.getProperties() == null) {
301       return;
302     }
303     List<Map<String, Object>> heatNetworkList =
304         (List<Map<String, Object>>) resource.getProperties().get("networks");
305
306     if (CollectionUtils.isEmpty(heatNetworkList)) {
307       return;
308     }
309
310     for (Map<String, Object> heatNetwork : heatNetworkList) {
311       getOrTranslatePortTemplate(heatFileName, heatOrchestrationTemplate,
312           heatNetwork.get(Constants.PORT_PROPERTY_NAME), serviceTemplate, translatedId, context,
313           novaNodeTemplate);
314     }
315
316   }
317
318   private void getOrTranslatePortTemplate(String heatFileName,
319                                           HeatOrchestrationTemplate heatOrchestrationTemplate,
320                                           Object port, ServiceTemplate serviceTemplate,
321                                           String novaServerResourceId, TranslationContext context,
322                                           NodeTemplate novaNodeTemplate) {
323     Optional<AttachedResourceId> attachedPortId = HeatToToscaUtil
324         .extractAttachedResourceId(heatFileName, heatOrchestrationTemplate, context, port);
325
326     if (!attachedPortId.isPresent()) {
327       return;
328     }
329
330     if (attachedPortId.get().isGetResource()) {
331       String resourceId = (String) attachedPortId.get().getEntityId();
332       Resource portResource =
333           HeatToToscaUtil.getResource(heatOrchestrationTemplate, resourceId, heatFileName);
334       if (!Arrays.asList(HeatResourcesTypes.NEUTRON_PORT_RESOURCE_TYPE.getHeatResource(),
335           HeatResourcesTypes.CONTRAIL_V2_VIRTUAL_MACHINE_INTERFACE_RESOURCE_TYPE.getHeatResource())
336           .contains(portResource.getType())) {
337         logger.warn("NovaServer connect to port resource with id : " + resourceId + " and type : "
338             + portResource.getType()
339             + ". This resource type is not supported, therefore the connection to the port is "
340             + "ignored. "
341             + "Supported types are: "
342             + HeatResourcesTypes.NEUTRON_PORT_RESOURCE_TYPE.getHeatResource() + ", "
343             + HeatResourcesTypes.CONTRAIL_V2_VIRTUAL_MACHINE_INTERFACE_RESOURCE_TYPE
344                 .getHeatResource());
345         return;
346       } else if (HeatResourcesTypes.CONTRAIL_V2_VIRTUAL_MACHINE_INTERFACE_RESOURCE_TYPE
347           .getHeatResource().equals(portResource.getType())) {
348         Map<String, Object> properties = portResource.getProperties();
349         if (!MapUtils.isEmpty(properties) && Objects.nonNull(properties.get("port_tuple_refs"))) {
350           novaNodeTemplate.getProperties().put("contrail_service_instance_ind", true);
351         }
352       }
353       Optional<String> translatedPortId = ResourceTranslationFactory.getInstance(portResource)
354           .translateResource(heatFileName, serviceTemplate, heatOrchestrationTemplate, portResource,
355               resourceId, context);
356       if (translatedPortId.isPresent()) {
357         NodeTemplate portNodeTemplate =
358             DataModelUtil.getNodeTemplate(serviceTemplate, translatedPortId.get());
359         addBindingReqFromPortToCompute(novaServerResourceId, portNodeTemplate);
360       } else {
361         logger.warn("NovaServer connect to port resource with id : " + resourceId + " and type : "
362             + portResource.getType()
363             + ". This resource type is not supported, therefore the connection to the port is "
364             + "ignored.");
365       }
366     }
367   }
368
369   /**
370    * Create local node type string.
371    *
372    * @param serviceTemplate      the service template
373    * @param properties           the properties
374    * @param resourceTranslatedId the resource translated id
375    * @return the string
376    */
377   public String createLocalNodeType(ServiceTemplate serviceTemplate, Map<String, Object> properties,
378                                     String resourceTranslatedId) {
379     NameExtractorService nodeTypeNameExtractor = new NameExtractorServiceImpl();
380     List<PropertyRegexMatcher> propertyRegexMatchers =
381         getPropertiesAndRegexMatchers(nodeTypeNameExtractor);
382     Optional<String> extractedNodeTypeName = nodeTypeNameExtractor
383         .extractNodeTypeNameByPropertiesPriority(properties, propertyRegexMatchers);
384
385     String nodeTypeName = ToscaConstants.NODES_PREFIX
386         + (extractedNodeTypeName.isPresent() ? extractedNodeTypeName.get()
387             : resourceTranslatedId.replace(".", "_"));
388     if (!isNodeTypeCreated(serviceTemplate, nodeTypeName)) {
389       DataModelUtil.addNodeType(serviceTemplate, nodeTypeName, createNodeType());
390     }
391     return nodeTypeName;
392   }
393
394   private List<PropertyRegexMatcher> getPropertiesAndRegexMatchers(
395       NameExtractorService nodeTypeNameExtractor) {
396     List<PropertyRegexMatcher> propertyRegexMatchers = new ArrayList<>();
397     propertyRegexMatchers.add(nodeTypeNameExtractor
398         .getPropertyRegexMatcher(Constants.NAME_PROPERTY_NAME,
399             Arrays.asList(".+_name$", ".+_names$", ".+_name_[0-9]+"), "_name"));
400     propertyRegexMatchers.add(nodeTypeNameExtractor
401         .getPropertyRegexMatcher("image", Collections.singletonList(".+_image_name$"),
402             "_image_name"));
403     propertyRegexMatchers.add(nodeTypeNameExtractor
404         .getPropertyRegexMatcher("flavor", Collections.singletonList(".+_flavor_name$"),
405             "_flavor_name"));
406     return propertyRegexMatchers;
407   }
408
409   private boolean isNodeTypeCreated(ServiceTemplate serviceTemplate, String nodeTypeName) {
410     return !MapUtils.isEmpty(serviceTemplate.getNode_types())
411         && Objects.nonNull(serviceTemplate.getNode_types().get(nodeTypeName));
412   }
413
414   private NodeType createNodeType() {
415     NodeType nodeType = new NodeType();
416     nodeType.setDerived_from(ToscaNodeType.NOVA_SERVER.getDisplayName());
417     return nodeType;
418   }
419 }