[VVP] Support pluggable data sources for preload data
[vvp/validation-scripts.git] / ice_validator / preload / data.py
1 from abc import ABC, abstractmethod
2 from pathlib import Path
3 from typing import Iterable, Any, Optional, Mapping
4
5 from preload.model import VnfModule
6
7
8 class AbstractPreloadInstance(ABC):
9     """
10     Represents the data source for a single instance of a preload for
11     any format.  The implementation of AbstractPreloadGenerator will
12     call the methods of this class to retrieve the necessary data
13     to populate the preload.  If a data element is not available,
14     then simply return ``None`` and a suitable placeholder will be
15     placed in the preload.
16     """
17
18     @property
19     @abstractmethod
20     def output_dir(self) -> Path:
21         """
22         Base output directory where the preload will be generated.  Please
23         note, that the generator may create nested directories under this
24         directory for the preload.
25
26         :return: Path to the desired output directory.  This directory
27                  and its parents will be created by the generator if
28                  it is not already present.
29         """
30         raise NotImplementedError()
31
32     @property
33     @abstractmethod
34     def module_label(self) -> str:
35         """
36         Identifier of the module.  This must match the base name of the
37         heat module (ex: if the Heat file name is base.yaml, then the label
38         is 'base'.
39
40         :return: string name of the module
41         """
42         raise NotImplementedError()
43
44     @property
45     @abstractmethod
46     def vf_module_name(self) -> Optional[str]:
47         """
48         :return: module name to populate in the preload if available
49         """
50         raise NotImplementedError()
51
52     @property
53     @abstractmethod
54     def flag_incompletes(self) -> bool:
55         """
56         If True, then the generator will modify the file name of any
57         generated preload to end with _incomplete.<ext> if any preload
58         value was not satisfied by the data source.  If False, then
59         the file name will be the same regardless of the completeness
60         of the preload.
61
62         :return: True if file names should denote preload incompleteness
63         """
64         raise NotImplementedError()
65
66     @property
67     @abstractmethod
68     def preload_basename(self) -> str:
69         """
70         Base name of the preload that will be used by the generator to create
71         the file name.
72         """
73         raise NotImplementedError()
74
75     @property
76     @abstractmethod
77     def vnf_name(self) -> Optional[str]:
78         """
79         :return: the VNF name to populate in the prelad if available
80         """
81         raise NotImplementedError()
82
83     @property
84     @abstractmethod
85     def vnf_type(self) -> Optional[str]:
86         """
87         The VNF Type must be match the values in SDC.  It is a concatenation
88         of <Service Instance Name>/<Resource Instance Name>.
89
90         :return: VNF Type to populate in the preload if available
91         """
92         raise NotImplementedError()
93
94     @property
95     @abstractmethod
96     def vf_module_model_name(self) -> Optional[str]:
97         """
98         :return: Module model name if available
99         """
100         raise NotImplementedError()
101
102     @abstractmethod
103     def get_availability_zone(self, index: int, param_name: str) -> Optional[str]:
104         """
105         Retrieve the value for the availability zone at requested zero-based
106         index (i.e. 0, 1, 2, etc.)
107
108         :param index:       index of availability zone (0, 1, etc.)
109         :param param_name:  Name of the parameter from Heat
110         :return:            value for the AZ if available
111         """
112         raise NotImplementedError()
113
114     @abstractmethod
115     def get_network_name(self, network_role: str, name_param: str) -> Optional[str]:
116         """
117         Retrieve the OpenStack name of the network for the given network role.
118
119         :param network_role:    Network role from Heat template
120         :param name_param:      Network name parameter from Heat
121         :return:                Name of the network if available
122         """
123         raise NotImplementedError()
124
125     @abstractmethod
126     def get_subnet_id(
127         self, network_role: str, ip_version: int, param_name: str
128     ) -> Optional[str]:
129         """
130         Retrieve the subnet's UUID for the given network and IP version (4 or 6).
131
132         :param network_role:    Network role from Heat template
133         :param ip_version:      IP Version (4 or 6)
134         :param param_name:      Parameter name from Heat
135         :return:                UUID of the subnet if available
136         """
137         raise NotImplementedError()
138
139     @abstractmethod
140     def get_subnet_name(
141         self, network_role: str, ip_version: int, param_name: str
142     ) -> Optional[str]:
143         """
144         Retrieve the OpenStack Subnet name for the given network role and IP version
145
146         :param network_role:    Network role from Heat template
147         :param ip_version:      IP Version (4 or 6)
148         :param param_name:      Parameter name from Heat
149         :return:                Name of the subnet if available
150         """
151         raise NotImplementedError()
152
153     @abstractmethod
154     def get_vm_name(self, vm_type: str, index: int, param_name: str) -> Optional[str]:
155         """
156         Retrieve the vm name for the given VM type and index.
157
158         :param vm_type:         VM Type from Heat template
159         :param index:           Zero-based index of the VM for the vm-type
160         :param param_name:      Parameter name from Heat
161         :return:                VM Name if available
162         """
163         raise NotImplementedError()
164
165     @abstractmethod
166     def get_floating_ip(
167         self, vm_type: str, network_role: str, ip_version: int, param_name: str
168     ) -> Optional[str]:
169         """
170         Retreive the floating IP for the VM and Port identified by VM Type,
171         Network Role, and IP Version.
172
173         :param vm_type:         VM Type from Heat template
174         :param network_role:    Network Role from Heat template
175         :param ip_version:      IP Version (4 or 6)
176         :param param_name:      Parameter name from Heat
177         :return: floating IP address if available
178         """
179         raise NotImplementedError()
180
181     @abstractmethod
182     def get_fixed_ip(
183         self, vm_type: str, network_role: str, ip_version: int, index: int, param: str
184     ) -> Optional[str]:
185         """
186         Retreive the fixed IP for the VM and Port identified by VM Type,
187         Network Role, IP Version, and index.
188
189         :param vm_type:         VM Type from Heat template
190         :param network_role:    Network Role from Heat template
191         :param ip_version:      IP Version (4 or 6)
192         :param index:           zero-based index for the IP for the given
193                                 VM Type, Network Role, IP Version combo
194         :param param_name:      Parameter name from Heat
195         :return: floating IP address if available
196         """
197         raise NotImplementedError()
198
199     @abstractmethod
200     def get_vnf_parameter(self, key: str, value: Any) -> Optional[Any]:
201         """
202         Retrieve the value for the given key.  These will be placed in the
203         tag-values/vnf parameters in the preload.  If a value was specified in
204         the environment packaged in the Heat for for the VNF module, then
205         that value will be  passed in ``value``.  This class can return
206         the value or ``None`` if it does not have a value for the given key.
207
208         :param key:     parameter name from Heat
209         :param value:   Value from Heat env file if it was assigned there;
210                         None otherwise
211         :return:        Returns the value for the object.  This should
212                         be a str, dict, or list.  The generator will
213                         format it properly based on the selected output format
214         """
215         raise NotImplementedError()
216
217     @abstractmethod
218     def get_additional_parameters(self) -> Mapping[str, Any]:
219         """
220         Return any additional parameters that should be added to the VNF parameters.
221
222         This can be useful if you want to duplicate paramters in tag values that are
223         also in the other sections (ex: VM names).
224
225         :return: dict of str to object mappings that the generator must add to
226                  the vnf_parameters/tag values
227         """
228         raise NotImplementedError()
229
230
231 class AbstractPreloadDataSource(ABC):
232     """
233     Represents a data source for a VNF preload data.  Implementations of this
234     class can be dynamically discovered if they are in a preload plugin module.
235     A module is considered a preload plugin module if it starts with
236     prelaod_ and is available as a top level module on Python's sys.path.
237
238     The ``get_module_preloads`` will be invoked for each module in
239     the VNF.  An instance of AbstractPreloadInstance must be returned for
240     each instance of the preload module that is to be created.
241
242     Parameters:
243         :param path:    The path to the configuration source selected
244                         in either the VVP GUI or command-line.  This
245                         may be a file or directory depending upon
246                         the source_type defined by this data source
247     """
248
249     def __init__(self, path: Path):
250         self.path = path
251
252     @classmethod
253     @abstractmethod
254     def get_source_type(cls) -> str:
255         """
256         If 'FILE' returned, then the config source will be a specific
257         file; If 'DIR', then the config source will be a directory
258         :return:
259         """
260         raise NotImplementedError()
261
262     @classmethod
263     @abstractmethod
264     def get_identifier(cls) -> str:
265         """
266         Identifier for the given data source. This is the value that
267         can be passed via --preload-source-type.
268
269         :return: short identifier for this data source type
270         """
271
272     @classmethod
273     @abstractmethod
274     def get_name(self) -> str:
275         """
276         Human readable name to describe the preload data source. It is
277         recommended not to exceed 50 characters.
278
279         :return: human readable name of the preload data source (ex: Environment Files)
280         """
281         raise NotImplementedError()
282
283     @abstractmethod
284     def get_module_preloads(
285         self, module: VnfModule
286     ) -> Iterable[AbstractPreloadInstance]:
287         """
288         For the  requested module, return an instance of AbstractPreloadInstance
289         for every preload module you wish to be created.
290
291         :param module:  Module of the VNF
292         :return:        iterable of preloads to create for the given module
293         """
294         raise NotImplementedError()
295
296
297 class BlankPreloadInstance(AbstractPreloadInstance):
298     """
299     Used to create blank preload templates.  VVP will always create
300     a template of a preload in the requested format with no data provided.
301     """
302
303     def __init__(self, output_dir: Path, module_name: str):
304         self._output_dir = output_dir
305         self._module_name = module_name
306
307     @property
308     def flag_incompletes(self) -> bool:
309         return False
310
311     @property
312     def preload_basename(self) -> str:
313         return self._module_name
314
315     @property
316     def vf_module_name(self) -> Optional[str]:
317         return None
318
319     def get_vm_name(self, vm_type: str, index: int, param_name: str) -> Optional[str]:
320         return None
321
322     def get_availability_zone(self, index: int, param_name: str) -> Optional[str]:
323         return None
324
325     @property
326     def output_dir(self) -> Path:
327         return self._output_dir
328
329     @property
330     def module_label(self) -> str:
331         return self._module_name
332
333     @property
334     def vnf_name(self) -> Optional[str]:
335         return None
336
337     @property
338     def vnf_type(self) -> Optional[str]:
339         return None
340
341     @property
342     def vf_module_model_name(self) -> Optional[str]:
343         return None
344
345     def get_network_name(self, network_role: str, name_param: str) -> Optional[str]:
346         return None
347
348     def get_subnet_id(
349         self, network_role: str, ip_version: int, param_name: str
350     ) -> Optional[str]:
351         return None
352
353     def get_subnet_name(
354         self, network_role: str, ip_version: int, param_name: str
355     ) -> Optional[str]:
356         return None
357
358     def get_floating_ip(
359         self, vm_type: str, network_role: str, ip_version: int, param_name: str
360     ) -> Optional[str]:
361         return None
362
363     def get_fixed_ip(
364         self, vm_type: str, network_role: str, ip_version: int, index: int, param: str
365     ) -> Optional[str]:
366         return None
367
368     def get_vnf_parameter(self, key: str, value: Any) -> Optional[Any]:
369         return None
370
371     def get_additional_parameters(self) -> Mapping[str, Any]:
372         return {}