[VVP] Support pluggable data sources for preload data
[vvp/validation-scripts.git] / ice_validator / preload_vnfapi / vnfapi_generator.py
1 # -*- coding: utf8 -*-
2 # ============LICENSE_START====================================================
3 # org.onap.vvp/validation-scripts
4 # ===================================================================
5 # Copyright © 2019 AT&T Intellectual Property. All rights reserved.
6 # ===================================================================
7 #
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
12 #
13 #             http://www.apache.org/licenses/LICENSE-2.0
14 #
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.
20 #
21 #
22 #
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
27 #
28 #             https://creativecommons.org/licenses/by/4.0/
29 #
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.
35 #
36 # ============LICENSE_END============================================
37
38 import json
39 import os
40 from pathlib import Path
41 from typing import Mapping
42
43 from preload.data import AbstractPreloadInstance
44 from preload.generator import (
45     get_json_template,
46     get_or_create_template,
47     AbstractPreloadGenerator,
48 )
49 from preload.model import VnfModule, Port
50
51 THIS_DIR = os.path.dirname(os.path.abspath(__file__))
52 DATA_DIR = os.path.join(THIS_DIR, "vnfapi_data")
53
54
55 def get_or_create_network_template(network_role, vm_networks):
56     """
57     If the network role already exists in vm_networks, then
58     return that otherwise create a blank template and return that
59     """
60     return get_or_create_template(
61         DATA_DIR, "network-role", network_role, vm_networks, "vm-network"
62     )
63
64
65 class VnfApiPreloadGenerator(AbstractPreloadGenerator):
66     @classmethod
67     def supports_output_passing(cls):
68         return False
69
70     @classmethod
71     def format_name(cls):
72         return "VNF-API"
73
74     @classmethod
75     def output_sub_dir(cls):
76         return "vnfapi"
77
78     def generate_module(
79         self,
80         vnf_module: VnfModule,
81         preload_data: AbstractPreloadInstance,
82         output_dir: Path,
83     ):
84         self.module_incomplete = False
85         template = get_json_template(DATA_DIR, "preload_template")
86         self._populate(template, preload_data, vnf_module)
87         incomplete = (
88             "_incomplete"
89             if preload_data.flag_incompletes and self.module_incomplete
90             else ""
91         )
92         filename = "{}{}.json".format(preload_data.preload_basename, incomplete)
93         outfile = output_dir.joinpath(filename)
94         with outfile.open("w") as f:
95             json.dump(template, f, indent=4)
96
97     def _populate(
98         self,
99         template: Mapping,
100         preload_data: AbstractPreloadInstance,
101         vnf_module: VnfModule,
102     ):
103         self._add_vnf_metadata(template, preload_data)
104         self._add_availability_zones(template, preload_data, vnf_module)
105         self._add_vnf_networks(template, preload_data, vnf_module)
106         self._add_vms(template, preload_data, vnf_module)
107         self._add_parameters(template, preload_data, vnf_module)
108
109     def _add_vnf_metadata(self, template: Mapping, preload: AbstractPreloadInstance):
110         vnf_meta = template["input"]["vnf-topology-information"][
111             "vnf-topology-identifier"
112         ]
113
114         vnf_meta["vnf-name"] = self.normalize(preload.vnf_name, "vnf_name")
115         vnf_meta["generic-vnf-type"] = self.normalize(
116             preload.vnf_type,
117             "vnf-type",
118             "VALUE FOR: Concatenation of <Service Name>/"
119             "<VF Instance Name> MUST MATCH SDC",
120         )
121         vnf_meta["vnf-type"] = self.normalize(
122             preload.vf_module_model_name,
123             "vf-module-model-name",
124             "VALUE FOR: <vfModuleModelName> from CSAR or SDC",
125         )
126
127     def _add_availability_zones(
128         self, template: Mapping, preload: AbstractPreloadInstance, vnf_module: VnfModule
129     ):
130         zones = template["input"]["vnf-topology-information"]["vnf-assignments"][
131             "availability-zones"
132         ]
133         for i, zone_param in enumerate(vnf_module.availability_zones):
134             zone = preload.get_availability_zone(i, zone_param)
135             zones.append({"availability-zone": self.normalize(zone, zone_param, index=i)})
136
137     def add_floating_ips(
138         self, network_template: dict, port: Port, preload: AbstractPreloadInstance
139     ):
140         # only one floating IP is really supported, in the preload model
141         # so for now we'll just use the last one.  We might revisit this
142         # and if multiple floating params exist, then come up with an
143         # approach to pick just one
144         for ip in port.floating_ips:
145             ip_value = preload.get_floating_ip(
146                 port.vm.vm_type, port.network.network_role, ip.ip_version, ip.param
147             )
148             key = "floating-ip" if ip.ip_version == 4 else "floating-ip-v6"
149             network_template[key] = self.normalize(ip_value, ip.param)
150
151     def add_fixed_ips(
152         self, network_template: dict, port: Port, preload: AbstractPreloadInstance
153     ):
154         for index, ip in port.fixed_ips_with_index:
155             ip_value = preload.get_fixed_ip(
156                 port.vm.vm_type,
157                 port.network.network_role,
158                 ip.ip_version,
159                 index,
160                 ip.param,
161             )
162             ip_value = self.normalize(ip_value, ip.param, index=index)
163             if ip.ip_version == 4:
164                 network_template["network-ips"].append({"ip-address": ip_value})
165                 network_template["ip-count"] += 1
166             else:
167                 network_template["network-ips-v6"].append({"ip-address": ip_value})
168                 network_template["ip-count-ipv6"] += 1
169
170     def _add_vnf_networks(
171         self, template: Mapping, preload: AbstractPreloadInstance, vnf_module: VnfModule
172     ):
173         networks = template["input"]["vnf-topology-information"]["vnf-assignments"][
174             "vnf-networks"
175         ]
176         for network in vnf_module.networks:
177             network_data = {
178                 "network-role": network.network_role,
179                 "network-name": self.normalize(
180                     preload.get_network_name(network.network_role, network.name_param),
181                     network.name_param,
182                     "VALUE FOR: network name for {}".format(network.name_param),
183                 ),
184             }
185             for subnet in network.subnet_params:
186                 subnet_id = preload.get_subnet_id(
187                     network.network_role, subnet.ip_version, subnet.param_name
188                 )
189                 if subnet_id:
190                     key = (
191                         "ipv6-subnet-id" if "_v6_" in subnet.param_name else "subnet-id"
192                     )
193                     network_data[key] = self.normalize(subnet_id, subnet.param_name)
194                 else:
195                     subnet_name = preload.get_subnet_name(
196                         network.network_role, subnet.ip_version, ""
197                     )
198                     key = (
199                         "ipv6-subnet-name"
200                         if "_v6_" in subnet.param_name
201                         else "subnet-name"
202                     )
203                     msg = "VALUE FOR: name for {}".format(subnet.param_name)
204                     value = self.normalize(
205                         subnet_name, subnet.param_name, alt_message=msg
206                     )
207                     network_data[key] = value
208             networks.append(network_data)
209
210     def _add_vms(
211         self, template: Mapping, preload: AbstractPreloadInstance, vnf_module: VnfModule
212     ):
213         vm_list = template["input"]["vnf-topology-information"]["vnf-assignments"][
214             "vnf-vms"
215         ]
216         for vm in vnf_module.virtual_machine_types:
217             vm_template = get_json_template(DATA_DIR, "vm")
218             vm_template["vm-type"] = vm.vm_type
219             vm_template["vm-count"] = vm.vm_count
220             for i, param in enumerate(sorted(vm.names)):
221                 name = preload.get_vm_name(vm.vm_type, i, param)
222                 vm_template["vm-names"]["vm-name"].append(self.normalize(name, param, index=i))
223             vm_list.append(vm_template)
224             vm_networks = vm_template["vm-networks"]
225             for port in vm.ports:
226                 role = port.network.network_role
227                 network_template = get_or_create_network_template(role, vm_networks)
228                 network_template["network-role"] = role
229                 network_template["network-role-tag"] = role
230                 network_template["use-dhcp"] = "Y" if port.uses_dhcp else "N"
231                 self.add_fixed_ips(network_template, port, preload)
232                 self.add_floating_ips(network_template, port, preload)
233
234     def _add_parameters(
235         self, template: Mapping, preload: AbstractPreloadInstance, vnf_module: VnfModule
236     ):
237         params = template["input"]["vnf-topology-information"]["vnf-parameters"]
238         for key, value in vnf_module.preload_parameters.items():
239             preload_value = preload.get_vnf_parameter(key, value)
240             value = preload_value or value
241             params.append(
242                 {
243                     "vnf-parameter-name": key,
244                     "vnf-parameter-value": self.normalize(value, key),
245                 }
246             )
247         for key, value in preload.get_additional_parameters().items():
248             params.append(
249                 {
250                     "vnf-parameter-name": key,
251                     "vnf-parameter-value": self.normalize(value, key),
252                 }
253             )