Add SO APIs to Nokia VNFM adapter
[vfc/nfvo/driver/vnfm/svnfm.git] / nokiav2 / driver / src / main / java / org / onap / vfc / nfvo / driver / vnfm / svnfm / nokia / packagetransformer / OnapR2HeatPackageBuilder.java
1 /*
2  * Copyright 2016-2017, Nokia Corporation
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  */
16
17 package org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.packagetransformer;
18
19 import com.google.common.collect.Sets;
20 import com.google.gson.*;
21 import java.util.HashMap;
22 import java.util.Map;
23 import java.util.Set;
24 import org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.vnfm.LifecycleManager;
25 import org.slf4j.Logger;
26 import org.yaml.snakeyaml.Yaml;
27
28 import static java.util.stream.Collectors.toSet;
29
30 import static org.onap.vfc.nfvo.driver.vnfm.svnfm.nokia.util.CbamUtils.*;
31 import static org.slf4j.LoggerFactory.getLogger;
32
33 /**
34  * Transforms a CBAM package into an ONAP package
35  */
36 public class OnapR2HeatPackageBuilder {
37     public static final String ETSI_MODIFIABLE_ATTRIBUTES_EXTENSTION = "etsi_modifiable_attributes_extenstion_";
38     public static final String IMAGE_NAME = "_image_name";
39     public static final String FLAVOR_NAME = "_flavor_name";
40     public static final String NET_ID = "_net_id";
41     private static Logger logger = getLogger(OnapR2HeatPackageBuilder.class);
42     public static final String BASE_ENV_FILE_NAME = "base.env";
43     public static final String BASE_YAML_FILE_NAME = "base.yaml";
44
45     private Set<String> autoManagedExtensions = Sets.newHashSet(LifecycleManager.ONAP_CSAR_ID, LifecycleManager.EXTERNAL_VNFM_ID);
46
47     Map<String, String> processVnfd(String cbamVnfd) {
48         Map<String, String> files = new HashMap<>();
49         JsonObject root = new Gson().toJsonTree(new Yaml().load(cbamVnfd)).getAsJsonObject();
50         JsonObject topologyTemplate = child(root, "topology_template");
51         JsonObject substitutionMappings = child(topologyTemplate, "substitution_mappings");
52         JsonObject capabilities = child(substitutionMappings, "capabilities");
53         JsonObject deploymentFlavour = child(capabilities, "deployment_flavour");
54         JsonArray policies = childElement(topologyTemplate, "policies").getAsJsonArray();
55         JsonObject manifest = new JsonObject();
56         manifest.addProperty("name", "ONAP VNF package");
57         manifest.addProperty("description", "");
58         JsonArray data = new JsonArray();
59         manifest.add("data", data);
60         JsonObject deploymentFlavorProperties = child(deploymentFlavour, "properties");
61         if (deploymentFlavorProperties.has("scaling_aspects")) {
62             JsonObject scalingAspects = child(deploymentFlavorProperties, "scaling_aspects");
63             for (Map.Entry<String, JsonElement> scalingAspect : scalingAspects.entrySet()) {
64                 processAspect(files, data, policies, scalingAspect.getKey(), childElement(scalingAspect.getValue().getAsJsonObject(), "max_scale_level").getAsLong());
65             }
66         }
67         processBaseIncrement(topologyTemplate, files, policies, data);
68         files.put("MANIFEST.json", new GsonBuilder().setPrettyPrinting().create().toJson(manifest));
69         return files;
70     }
71
72     private void processBaseIncrement(JsonObject topologyTemplate, Map<String, String> files, JsonArray policies, JsonArray data) {
73         StringBuilder envContent = prepareEvnContent();
74         StringBuilder yamlContent = prepareYamlContent();
75         if (topologyTemplate.has("node_templates")) {
76             JsonObject nodeTemplates = child(topologyTemplate, "node_templates");
77             processEcps(nodeTemplates, envContent, yamlContent);
78             for (Map.Entry<String, JsonElement> vdu : filterType(nodeTemplates, "tosca.nodes.nfv.VDU")) {
79                 addImageAndFlavor(envContent, yamlContent, vdu);
80             }
81         }
82         processModifiableAttributes(topologyTemplate, envContent, yamlContent);
83         data.add(buildManifestEntry(BASE_ENV_FILE_NAME, BASE_YAML_FILE_NAME, true));
84         files.put(BASE_ENV_FILE_NAME, envContent.toString());
85         files.put(BASE_YAML_FILE_NAME, yamlContent.toString());
86     }
87
88     private void processModifiableAttributes(JsonObject topologyTemplate, StringBuilder envContent, StringBuilder yamlContent) {
89         JsonObject capabilities = child(child(topologyTemplate, "substitution_mappings"), "capabilities");
90         if (capabilities.has("vnf")) {
91             JsonObject vnf = child(capabilities, "vnf");
92             if (vnf.has("properties")) {
93                 JsonObject properties = child(vnf, "properties");
94                 if (properties.has("modifiable_attributes")) {
95                     JsonObject modifiableAttributes = child(properties, "modifiable_attributes");
96                     if (modifiableAttributes.has("extensions")) {
97                         JsonObject extensions = child(modifiableAttributes, "extensions");
98                         for (Map.Entry<String, JsonElement> extension : extensions.entrySet()) {
99                             if (!autoManagedExtensions.contains(extension.getKey())) {
100                                 addParameter(yamlContent, envContent, ETSI_MODIFIABLE_ATTRIBUTES_EXTENSTION + extension.getKey(), "Modifiable attribute", "Modifiable attribute for " + extension.getKey());
101                             }
102                         }
103                     }
104                 }
105             }
106         }
107     }
108
109     public static Set<Map.Entry<String, JsonElement>> filterType(JsonObject nodeTemplates, String type) {
110         return nodeTemplates.entrySet().stream().filter(e -> e.getValue().getAsJsonObject().get("type").equals(type)).collect(toSet());
111     }
112
113     private void processEcps(JsonObject nodeTemplates, StringBuilder envContent, StringBuilder yamlContent) {
114         for (Map.Entry<String, JsonElement> node : filterType(nodeTemplates, "tosca.nodes.nfv.ECP")) {
115             envContent.append("  " + node.getKey() + NET_ID + ": PUT YOUR NETWORK ID HERE\n");
116             addYamlParameter(yamlContent, node.getKey() + NET_ID, "Network id", "Network identifier for " + node.getKey() + " ECP");
117         }
118     }
119
120     private StringBuilder prepareYamlContent() {
121         StringBuilder yamlContent = new StringBuilder();
122         yamlContent.append("heat_template_version: 2013-05-23\n");
123         yamlContent.append("parameters:\n");
124         return yamlContent;
125     }
126
127     private void processAspect(Map<String, String> files, JsonArray data, JsonArray policies, String aspectName, long maxScaleLevel) {
128         JsonObject aspect = locateAspect(locateHeatPolicy(policies), aspectName);
129         StringBuilder envContent = prepareEvnContent();
130         StringBuilder yamlContent = prepareYamlContent();
131         if (aspect.has("vdus")) {
132             processMapping(aspect, envContent, yamlContent);
133         }
134         if (maxScaleLevel > 1001) {
135             throw buildFatalFailure(logger, "Refusing to create more than 1001 scaling levels");
136         }
137         envContent.append("  etsi.scalingAspectId: "+ aspectName + "\n");
138         for (int scaleIndex = 0; scaleIndex < maxScaleLevel; scaleIndex++) {
139             String envFileName = "module_" + aspectName + "_" + scaleIndex + ".env";
140             files.put(envFileName, envContent.toString());
141             String yamlFileName = "module_" + aspectName + "_" + scaleIndex + ".yaml";
142             files.put(yamlFileName, yamlContent.toString());
143             data.add(buildManifestEntry(envFileName, yamlFileName, false));
144         }
145     }
146
147     private StringBuilder prepareEvnContent() {
148         StringBuilder envContent = new StringBuilder();
149         envContent.append("parameters:\n");
150         return envContent;
151     }
152
153     private JsonObject buildManifestEntry(String envFileName, String yamlFileName, boolean base) {
154         JsonObject manifestEntry = new JsonObject();
155         manifestEntry.addProperty("file", yamlFileName);
156         manifestEntry.addProperty("type", "HEAT");
157         manifestEntry.addProperty("isBase", Boolean.toString(base));
158         JsonArray envEntries = new JsonArray();
159         manifestEntry.add("data", envEntries);
160         JsonObject envEntry = new JsonObject();
161         envEntries.add(envEntry);
162         envEntry.addProperty("file", envFileName);
163         envEntry.addProperty("type", "HEAT_ENV");
164         return manifestEntry;
165     }
166
167     private void processMapping(JsonObject mapping, StringBuilder envContent, StringBuilder yamlContent) {
168         for (Map.Entry<String, JsonElement> vdusElement : child(mapping, "vdus").entrySet()) {
169             addImageAndFlavor(envContent, yamlContent, vdusElement);
170         }
171         if (mapping.has("externalConnectionPoints")) {
172             for (Map.Entry<String, JsonElement> externalConnectionPoints : child(mapping, "externalConnectionPoints").entrySet()) {
173                 addParameter(yamlContent, envContent, externalConnectionPoints.getKey() + "_net_id", "Network id", "Network to be used for " + externalConnectionPoints.getKey() + " ECP");
174                 addParameter(yamlContent, envContent, externalConnectionPoints.getKey() + "_subnet_id", "Subnet id", "Subnet to be used for " + externalConnectionPoints.getKey() + " ECP");
175             }
176         }
177     }
178
179     private void addImageAndFlavor(StringBuilder envContent, StringBuilder yamlContent, Map.Entry<String, JsonElement> vdusElement) {
180         String vdu = vdusElement.getKey();
181         addParameter(yamlContent, envContent, vdu + IMAGE_NAME, "Image name or identifier", "Image to be used for " + vdu + " VDU");
182         addParameter(yamlContent, envContent, vdu + FLAVOR_NAME, "Flavor name or identifier", "Flavor to be used for " + vdu + " VDU");
183     }
184
185     private void addParameter(StringBuilder yamlContent, StringBuilder envContent, String key, String label, String description) {
186         addYamlParameter(yamlContent, key, label, description);
187         envContent.append("  " + key + ": PUT YOUR " + label.toUpperCase() + " HERE\n");
188     }
189
190     private void addYamlParameter(StringBuilder yamlContent, String key, String label, String description) {
191         yamlContent.append("  " + key + ":\n");
192         yamlContent.append("    type: string\n");
193         yamlContent.append("    label: " + label + "\n");
194         yamlContent.append("    description: " + description + "\n");
195     }
196
197     private JsonObject locateHeatPolicy(JsonArray policies) {
198         for (int index = 0; index < policies.size(); index++) {
199             JsonObject c = policies.get(index).getAsJsonObject();
200             JsonObject policy = c.getAsJsonObject().entrySet().iterator().next().getValue().getAsJsonObject();
201             if ("tosca.policies.nfv.HeatMapping".equals(childElement(policy, "type").getAsString())) {
202                 return policy;
203             }
204         }
205         throw buildFatalFailure(logger, "The heat_mapping section is missing from VNFD");
206     }
207
208     private JsonObject locateAspect(JsonObject policy, String aspectName) {
209         for (Map.Entry<String, JsonElement> aspect : child(child(policy, "properties"), "aspects").entrySet()) {
210             if (aspect.getKey().equals(aspectName)) {
211                 return aspect.getValue().getAsJsonObject();
212             }
213         }
214         throw buildFatalFailure(logger, "Unable to locate " + aspectName + " in heat policy");
215     }
216 }