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
46 def represent_ordered_dict(dumper, data):
49 for item_key, item_value in data.items():
50 node_key = dumper.represent_data(item_key)
51 node_value = dumper.represent_data(item_value)
53 value.append((node_key, node_value))
55 return yaml.nodes.MappingNode(u"tag:yaml.org,2002:map", value)
58 def get_json_template(template_dir, template_name):
59 template_name = template_name + ".json"
60 with open(os.path.join(template_dir, template_name)) as f:
61 return json.loads(f.read())
64 def get_or_create_template(template_dir, key, value, sequence, template_name):
66 Search a sequence of dicts where a given key matches value. If
67 found, then it returns that item. If not, then it loads the
68 template identified by template_name, adds it ot the sequence, and
72 if item[key] == value:
74 new_template = get_json_template(template_dir, template_name)
75 sequence.append(new_template)
79 def yield_by_count(sequence):
81 Iterates through sequence and yields each item according to its __count__
82 attribute. If an item has a __count__ of it will be returned 3 times
83 before advancing to the next item in the sequence.
85 :param sequence: sequence of dicts (must contain __count__)
86 :returns: generator of tuple key, value pairs
88 for key, value in sequence.items():
89 for i in range(value["__count__"]):
95 Optionally used by the preload generator to wrap items in the preload
96 that need to be replaced by end users
99 return "VALUE FOR: {}".format(param) if param else ""
102 class AbstractPreloadGenerator(ABC):
104 All preload generators must inherit from this class and implement the
107 Preload generators are automatically discovered at runtime via a plugin
108 architecture. The system path is scanned looking for modules with the name
109 preload_*, then all non-abstract classes that inherit from AbstractPreloadGenerator
110 are registered as preload plugins
113 :param vnf: Instance of Vnf that contains the preload data
114 :param base_output_dir: Base directory to house the preloads. All preloads
115 must be written to a subdirectory under this directory
118 def __init__(self, vnf, base_output_dir, preload_env):
119 self.preload_env = preload_env
121 self.current_module = None
122 self.current_module_env = {}
123 self.base_output_dir = base_output_dir
125 self.module_incomplete = False
129 def format_name(cls):
131 String name to identify the format (ex: VN-API, GR-API)
133 raise NotImplementedError()
137 def output_sub_dir(cls):
139 String sub-directory name that will appear under ``base_output_dir``
141 raise NotImplementedError()
145 def supports_output_passing(cls):
147 Some preload methods allow automatically mapping output parameters in the
148 base module to the input parameter of other modules. This means these
149 that the incremental modules do not need these base module outputs in their
152 At this time, VNF-API does not support output parameter passing, but
155 If this is true, then the generator will call Vnf#filter_output_params
156 after the preload module for the base module has been created
158 raise NotImplementedError()
161 def generate_module(self, module, output_dir):
163 Create the preloads and write them to ``output_dir``. This
164 method is responsible for generating the content of the preload and
165 writing the file to disk.
167 raise NotImplementedError()
170 # handle the base module first
171 print("\nGenerating {} preloads".format(self.format_name()))
172 self.generate_environments(self.vnf.base_module)
173 if self.supports_output_passing():
174 self.vnf.filter_base_outputs()
175 for mod in self.vnf.incremental_modules:
176 self.generate_environments(mod)
178 def replace(self, param_name, alt_message=None, single=False):
179 value = self.get_param(param_name, single)
180 value = None if value == "CHANGEME" else value
184 self.module_incomplete = True
185 return alt_message or replace(param_name)
187 def start_module(self, module, env):
188 """Initialize/reset the environment for the module"""
189 self.current_module = module
190 self.current_module_env = env
191 self.module_incomplete = False
194 def generate_environments(self, module):
196 Generate a preload for the given module in all available environments
197 in the ``self.preload_env``. This will invoke the abstract
198 generate_module once for each available environment **and** an
199 empty environment to create a blank template.
201 :param module: module to generate for
203 print("\nGenerating Preloads for {}".format(module))
205 print("... generating blank template")
206 self.start_module(module, {})
207 blank_preload_dir = self.make_preload_dir(self.base_output_dir)
208 self.generate_module(module, blank_preload_dir)
209 self.generate_preload_env(module, blank_preload_dir)
211 for env in self.preload_env.environments:
212 output_dir = self.make_preload_dir(env.base_dir / "preloads")
214 "... generating preload for env ({}) to {}".format(
218 self.start_module(module, env.get_module(module.label))
219 self.generate_module(module, output_dir)
221 def make_preload_dir(self, base_dir):
222 path = os.path.join(base_dir, self.output_sub_dir())
223 if not os.path.exists(path):
224 os.makedirs(path, exist_ok=True)
228 def generate_preload_env(module, blank_preload_dir):
230 Create a .env template suitable for completing and using for
231 preload generation from env files.
233 yaml.add_representer(OrderedDict, represent_ordered_dict)
234 output_dir = os.path.join(blank_preload_dir, "preload_env")
235 env_file = os.path.join(output_dir, "{}.env".format(module.vnf_name))
236 defaults_file = os.path.join(output_dir, "defaults.yaml")
237 if not os.path.exists(output_dir):
238 os.makedirs(output_dir, exist_ok=True)
239 with open(env_file, "w") as f:
240 yaml.dump(module.env_template, f)
241 if not os.path.exists(defaults_file):
242 with open(defaults_file, "w") as f:
243 yaml.dump({"vnf_name": "CHANGEME"}, f)
245 def get_param(self, param_name, single):
247 Retrieves the value for the given param if it exists. If requesting a
248 single item, and the parameter is tied to a list then only one item from
249 the list will be returned. For each subsequent call with the same parameter
250 it will iterate/rotate through the values in that list. If single is False
251 then the full list will be returned.
253 :param param_name: name of the parameter
254 :param single: If True returns single value from lists otherwises the full
255 list. This has no effect on non-list values
257 value = self.env_cache.get(param_name)
259 value = self.current_module_env.get(param_name)
260 if isinstance(value, list):
263 self.env_cache[param_name] = value
264 if value and single and isinstance(value, list):