2 # ============LICENSE_START====================================================
3 # org.onap.vvp/validation-scripts
4 # ===================================================================
5 # Copyright © 2019 AT&T Intellectual Property. All rights reserved.
6 # ===================================================================
8 # Unless otherwise specified, all software contained herein is licensed
9 # under the Apache License, Version 2.0 (the "License");
10 # you may not use this software except in compliance with the License.
11 # You may obtain a copy of the License at
13 # http://www.apache.org/licenses/LICENSE-2.0
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
23 # Unless otherwise specified, all documentation contained herein is licensed
24 # under the Creative Commons License, Attribution 4.0 Intl. (the "License");
25 # you may not use this documentation except in compliance with the License.
26 # You may obtain a copy of the License at
28 # https://creativecommons.org/licenses/by/4.0/
30 # Unless required by applicable law or agreed to in writing, documentation
31 # distributed under the License is distributed on an "AS IS" BASIS,
32 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33 # See the License for the specific language governing permissions and
34 # limitations under the License.
36 # ============LICENSE_END============================================
40 from abc import ABC, abstractmethod
41 from collections import OrderedDict
42 from pathlib import Path
46 from preload.data import (
47 AbstractPreloadDataSource,
48 AbstractPreloadInstance,
51 from preload.model import VnfModule, Vnf
54 def represent_ordered_dict(dumper, data):
57 for item_key, item_value in data.items():
58 node_key = dumper.represent_data(item_key)
59 node_value = dumper.represent_data(item_value)
61 value.append((node_key, node_value))
63 return yaml.nodes.MappingNode(u"tag:yaml.org,2002:map", value)
66 def get_json_template(template_dir, template_name):
67 template_name = template_name + ".json"
68 with open(os.path.join(template_dir, template_name)) as f:
69 return json.loads(f.read())
72 def get_or_create_template(template_dir, key, value, sequence, template_name):
74 Search a sequence of dicts where a given key matches value. If
75 found, then it returns that item. If not, then it loads the
76 template identified by template_name, adds it ot the sequence, and
80 if item[key] == value:
82 new_template = get_json_template(template_dir, template_name)
83 sequence.append(new_template)
87 def replace(param, index=None):
89 Optionally used by the preload generator to wrap items in the preload
90 that need to be replaced by end users
91 :param param: parameter name
92 :param index: optional index (int or str) of the parameter
94 if (param.endswith("_names") or param.endswith("_ips")) and index is not None:
95 param = "{}[{}]".format(param, index)
96 return "VALUE FOR: {}".format(param) if param else ""
99 class AbstractPreloadGenerator(ABC):
101 All preload generators must inherit from this class and implement the
104 Preload generators are automatically discovered at runtime via a plugin
105 architecture. The system path is scanned looking for modules with the name
106 preload_*, then all non-abstract classes that inherit from AbstractPreloadGenerator
107 are registered as preload plugins
110 :param vnf: Instance of Vnf that contains the preload data
111 :param base_output_dir: Base directory to house the preloads. All preloads
112 must be written to a subdirectory under this directory
113 :param data_source: Source data for preload population
117 self, vnf: Vnf, base_output_dir: Path, data_source: AbstractPreloadDataSource
119 self.data_source = data_source
121 self.base_output_dir = base_output_dir
122 self.module_incomplete = False
126 def format_name(cls):
128 String name to identify the format (ex: VN-API, GR-API)
130 raise NotImplementedError()
134 def output_sub_dir(cls):
136 String sub-directory name that will appear under ``base_output_dir``
138 raise NotImplementedError()
142 def supports_output_passing(cls):
144 Some preload methods allow automatically mapping output parameters in the
145 base module to the input parameter of other modules. This means these
146 that the incremental modules do not need these base module outputs in their
149 At this time, VNF-API does not support output parameter passing, but
152 If this is true, then the generator will call Vnf#filter_output_params
153 after the preload module for the base module has been created
155 raise NotImplementedError()
158 def generate_module(self, module: VnfModule, preload: AbstractPreloadInstance, output_dir: Path):
160 Create the preloads. This method is responsible for generating the
161 content of the preload and writing the file to disk.
163 raise NotImplementedError()
166 # handle the base module first
167 print("\nGenerating {} preloads".format(self.format_name()))
168 if self.vnf.base_module:
169 self.generate_preloads(self.vnf.base_module)
170 if self.supports_output_passing():
171 self.vnf.filter_base_outputs()
172 for mod in self.vnf.incremental_modules:
173 self.generate_preloads(mod)
175 def start_module(self):
176 """Initialize/reset the environment for the module"""
177 self.module_incomplete = False
179 def generate_preloads(self, module):
181 Generate a preload for the given module in all available environments
182 in the ``self.preload_env``. This will invoke the abstract
183 generate_module once for each available environment **and** an
184 empty environment to create a blank template.
186 :param module: module to generate for
188 print("\nGenerating Preloads for {}".format(module))
190 print("... generating blank template")
192 preload = BlankPreloadInstance(Path(self.base_output_dir), module.label)
193 blank_preload_dir = self.make_preload_dir(preload)
194 self.generate_module(module, preload, blank_preload_dir)
195 self.generate_preload_env(module, preload)
198 preloads = self.data_source.get_module_preloads(module)
199 for preload in preloads:
200 output_dir = self.make_preload_dir(preload)
202 "... generating preload for {} to {}".format(
203 preload.module_label, output_dir
207 self.generate_module(module, preload, output_dir)
209 def make_preload_dir(self, preload: AbstractPreloadInstance):
210 preload_dir = preload.output_dir.joinpath(self.output_sub_dir())
211 preload_dir.mkdir(parents=True, exist_ok=True)
215 def generate_preload_env(module: VnfModule, preload: AbstractPreloadInstance):
217 Create a .env template suitable for completing and using for
218 preload generation from env files.
220 yaml.add_representer(OrderedDict, represent_ordered_dict)
221 output_dir = preload.output_dir.joinpath("preload_env")
222 env_file = output_dir.joinpath("{}.env".format(module.label))
223 defaults_file = output_dir.joinpath("defaults.yaml")
224 output_dir.mkdir(parents=True, exist_ok=True)
225 with env_file.open("w") as f:
226 yaml.dump(module.env_template, f)
227 if not defaults_file.exists():
228 with defaults_file.open("w") as f:
229 yaml.dump({"vnf_name": "CHANGEME"}, f)
231 def normalize(self, preload_value, param_name, alt_message=None, index=None):
232 preload_value = None if preload_value == "CHANGEME" else preload_value
236 self.module_incomplete = True
237 return alt_message or replace(param_name, index)