update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / common / python / rift / mano / config_data / config.py
1 ###########################################################################
2 # Copyright 2016 RIFT.io Inc #
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
18 import abc
19 import json
20 import os
21 import yaml
22
23 from gi.repository import ProjectNsdYang as NsdYang
24 from gi.repository import ProjectVnfdYang as VnfdYang
25
26
27 class InitialConfigReadError(Exception):
28 pass
29
30
31 class InitialConfigMethodError(Exception):
32 pass
33
34
35 class InitialConfigPrimitiveReader(object):
36 """ Reader for the VNF Initial Config Input Data
37
38 This class interprets the Initial Config Primitive
39 Input data and constructs inital config primitive
40 protobuf messages.
41
42 The reason for creating a new format is to keep the structure
43 as dead-simple as possible for readability.
44
45 The structure (not serialization format) is defined as the
46 following.
47
48 [
49 {
50 "name": <primitive_name>,
51 "parameter": {
52 "hostname": "pe1"
53 "pass": "6windos"
54 ...
55 }
56 }
57 ...
58 ]
59
60 """
61 def __init__(self, primitive_input):
62 self._primitives = []
63
64 self._parse_input_data(primitive_input)
65
66 def _parse_input_data(self, input_dict):
67 for seq, cfg in enumerate(input_dict):
68 if "name" not in cfg:
69 raise InitialConfigReadError("Initial config primitive must have a name")
70
71 name = cfg["name"]
72
73 new_primitive = self. get_initial_config_primitive(seq=seq, name=name)
74 self._primitives.append(new_primitive)
75 if "parameter" in cfg:
76 for key, val in cfg["parameter"].items():
77 new_primitive.parameter.add(name=key, value=val)
78
79 @abc.abstractmethod
80 def get_initial_config_primitive(self, seq, name):
81 '''Override in sub class to provide the correct yang model'''
82 raise InitialConfigMethodError(
83 "InitialConfigPrimitiveReader Calling abstract class method")
84
85 @property
86 def primitives(self):
87 """ Returns a copy of the read inital config primitives"""
88 return [prim.deep_copy() for prim in self._primitives]
89
90 @classmethod
91 def from_yaml_file_hdl(cls, file_hdl):
92 """ Create a instance of InitialConfigPrimitiveFileData
93 by reading a YAML file handle.
94
95 Arguments:
96 file_hdl - A file handle which contains serialized YAML which
97 follows the documented structure.
98
99 Returns:
100 A new InitialConfigPrimitiveFileData() instance
101
102 Raises:
103 InitialConfigReadError: Input Data was malformed or could not be read
104 """
105 try:
106 input_dict = yaml.safe_load(file_hdl)
107 except yaml.YAMLError as e:
108 raise InitialConfigReadError(e)
109
110 return cls(input_dict)
111
112
113 class VnfInitialConfigPrimitiveReader(InitialConfigPrimitiveReader):
114 '''Class to read the VNF initial config primitives'''
115
116 def __init__(self, primitive_input):
117 super(VnfInitialConfigPrimitiveReader, self).__init__(primitive_input)
118
119 def get_initial_config_primitive(self, seq, name):
120 return VnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd_VnfConfiguration_InitialConfigPrimitive(seq=seq, name=name)
121
122
123 class NsInitialServicePrimitiveReader(InitialConfigPrimitiveReader):
124 '''Class to read the NS initial config primitives'''
125
126 def __init__(self, primitive_input):
127 super(NsInitialServicePrimitiveReader, self).__init__(primitive_input)
128
129 def get_initial_config_primitive(self, seq, name):
130 return NsdYang.YangData_Nsd_NsdCatalog_Nsd_InitialServicePrimitive(seq=seq, name=name)
131
132
133 class ConfigPrimitiveConvertor(object):
134 PARAMETER = "parameter"
135 PARAMETER_GROUP = "parameter_group"
136 SERVICE_PRIMITIVE = "service_primitive"
137 INITIAL_CONFIG_PRIMITIVE = "initial_config_primitive"
138
139 def _extract_param(self, param, field="default_value"):
140 key = param.name
141 value = getattr(param, field, None)
142
143 if value is not None:
144 setattr(param, field, None)
145
146 return key, value
147
148 def _extract_parameters(self, parameters, input_data, field="default_value"):
149 input_data[self.PARAMETER] = {}
150 for param in parameters:
151 key, value = self._extract_param(param, field)
152
153 if value is None:
154 continue
155
156 input_data[self.PARAMETER][key] = value
157
158 if not input_data[self.PARAMETER]:
159 del input_data[self.PARAMETER]
160
161 def _extract_parameter_group(self, param_groups, input_data):
162 input_data[self.PARAMETER_GROUP] = {}
163 for param_group in param_groups:
164 input_data[self.PARAMETER_GROUP][param_group.name] = {}
165 for param in param_group.parameter:
166 key, value = self._extract_param(param)
167
168 if value is None:
169 continue
170
171 input_data[self.PARAMETER_GROUP][param_group.name][key] = value
172
173 if not input_data[self.PARAMETER_GROUP]:
174 del input_data[self.PARAMETER_GROUP]
175
176 def extract_config(self,
177 config_primitives=None,
178 initial_configs=None,
179 format="yaml"):
180 input_data = {}
181
182 if config_primitives:
183 input_data[self.SERVICE_PRIMITIVE] = {}
184 for config_primitive in config_primitives:
185 input_data[self.SERVICE_PRIMITIVE][config_primitive.name] = {}
186 self._extract_parameters(
187 config_primitive.parameter,
188 input_data[self.SERVICE_PRIMITIVE][config_primitive.name])
189
190 try:
191 self._extract_parameter_group(
192 config_primitive.parameter_group,
193 input_data[self.SERVICE_PRIMITIVE][config_primitive.name])
194 except AttributeError:
195 pass
196
197 if not input_data[self.SERVICE_PRIMITIVE][config_primitive.name]:
198 del input_data[self.SERVICE_PRIMITIVE][config_primitive.name]
199
200 if not input_data[self.SERVICE_PRIMITIVE]:
201 del input_data[self.SERVICE_PRIMITIVE]
202
203
204 if initial_configs:
205 input_data[self.INITIAL_CONFIG_PRIMITIVE] = []
206 for in_config_primitive in initial_configs:
207 primitive = {}
208 self._extract_parameters(
209 in_config_primitive.parameter,
210 primitive,
211 field="value")
212
213 if primitive:
214 input_data[self.INITIAL_CONFIG_PRIMITIVE].append(
215 {
216 "name": in_config_primitive.name,
217 self.PARAMETER: primitive[self.PARAMETER],
218 }
219 )
220
221 if not input_data[self.INITIAL_CONFIG_PRIMITIVE]:
222 del input_data[self.INITIAL_CONFIG_PRIMITIVE]
223
224 if len(input_data):
225 if format == "json":
226 return json.dumps(input_data)
227 elif format == "yaml":
228 return yaml.dump(input_data, default_flow_style=False)
229 else:
230 return ''
231
232 def extract_nsd_config(self, nsd, format="yaml"):
233 config_prim = None
234 try:
235 config_prim = nsd.service_primitive
236 except AttributeError:
237 pass
238
239 initial_conf = None
240 try:
241 initial_conf = nsd.initial_service_primitive
242 except AttributeError:
243 pass
244
245 return self.extract_config(
246 config_primitives=config_prim,
247 initial_configs=initial_conf,
248 format=format)
249
250 def extract_vnfd_config(self, vnfd, format="yaml"):
251 config_prim = None
252 try:
253 config_prim = vnfd.vnf_configuration.config_primitive
254 except AttributeError:
255 pass
256
257 initial_conf = None
258 try:
259 initial_conf = vnfd.vnf_configuration.initial_config_primitive
260 except AttributeError:
261 pass
262
263 return self.extract_config(
264 config_primitives=config_prim,
265 initial_configs=initial_conf,
266 format=format)
267
268 def merge_params(self, parameters, input_config, field="default_value"):
269 for param in parameters:
270 try:
271 setattr(param, field, input_config[param.name])
272 except KeyError:
273 pass
274
275 def add_nsd_initial_config(self, nsd_init_cfg_prim_msg, input_data):
276 """ Add initial service primitives from NS Initial Config Input Data
277
278 Arguments:
279 nsd_init_cfg_prim_msg - manotypes:nsd/initial_config_primitive pb msg
280 ns_input_data - NsInitialConfigPrimitiveReader documented input data
281
282 Raises:
283 InitialConfigReadError: VNF input data was malformed
284 """
285 if self.INITIAL_CONFIG_PRIMITIVE in input_data:
286 ns_input_data = input_data[self.INITIAL_CONFIG_PRIMITIVE]
287
288 reader = NsInitialServicePrimitiveReader(ns_input_data)
289 for prim in reader.primitives:
290 nsd_init_cfg_prim_msg.append(prim)
291
292 def merge_nsd_initial_config(self, nsd, input_data):
293 try:
294 for service_primitive in nsd.initial_service_primitive:
295 for cfg in input_data[self.INITIAL_CONFIG_PRIMITIVE]:
296 if cfg['name'] == service_primitive.name:
297 self.merge_params(
298 service_primitive.parameter,
299 cfg[self.PARAMETER],
300 field="value")
301 break
302
303 except AttributeError as e:
304 self._log.debug("Did not find initial-service-primitive for NSD {}: {}".
305 format(nsd.name, e))
306
307 def merge_nsd_config(self, nsd, input_data):
308 for service_primitive in nsd.service_primitive:
309 try:
310 cfg = input_data[self.SERVICE_PRIMITIVE][service_primitive.name]
311 except KeyError:
312 continue
313
314 self.merge_params(
315 service_primitive.parameter,
316 cfg[self.PARAMETER])
317
318 for param_group in service_primitive.parameter_group:
319 self.merge_params(
320 param_group.parameter,
321 cfg[self.PARAMETER_GROUP][param_group.name])
322
323 def add_vnfd_initial_config(self, vnfd_init_cfg_prim_msg, input_data):
324 """ Add initial config primitives from VNF Initial Config Input Data
325
326 Arguments:
327 vnfd_init_cfg_prim_msg - manotypes:vnf-configuration/initial_config_primitive pb msg
328 vnf_input_data - VnfInitialConfigPrimitiveReader documented input data
329
330 Raises:
331 InitialConfigReadError: VNF input data was malformed
332 """
333 if self.INITIAL_CONFIG_PRIMITIVE in input_data:
334 vnf_input_data = input_data[self.INITIAL_CONFIG_PRIMITIVE]
335
336 reader = VnfInitialConfigPrimitiveReader(vnf_input_data)
337 for prim in reader.primitives:
338 vnfd_init_cfg_prim_msg.append(prim)
339
340 def merge_vnfd_config(self, vnfd, input_data):
341 for config_primitive in vnfd.vnf_configuration.config_primitive:
342 try:
343 cfg = input_data[self.SERVICE_PRIMITIVE][config_primitive.name]
344 except KeyError:
345 continue
346
347 self.merge_params(
348 config_primitive.parameter,
349 cfg[self.PARAMETER])
350
351
352 class ConfigStore(object):
353 """Convenience class that fetches all the instance related data from the
354 $RIFT_VAR_ROOT/launchpad/libs directory.
355 """
356
357 def __init__(self, log):
358 """
359 Args:
360 log : Log handle.
361 """
362 self._log = log
363 self.converter = ConfigPrimitiveConvertor()
364
365 def merge_vnfd_config(self,project_name, nsd_id, vnfd, member_vnf_index):
366 """Merges the vnfd config from the config directory.
367
368 Args:
369 nsd_id (str): Id of the NSD object
370 vnfd_msg : VNFD pb message containing the VNFD id and
371 the member index ref.
372 """
373 nsd_archive = os.path.join(
374 os.getenv('RIFT_VAR_ROOT'),
375 "launchpad/packages/vnfd/",
376 project_name,
377 vnfd.id,
378 "vnf_config")
379
380 self._log.info("Looking for config from the archive {}".format(nsd_archive))
381
382 if not os.path.exists(nsd_archive):
383 return
384
385 config_file = os.path.join(nsd_archive,
386 "{}__{}.yaml".format(vnfd.id, member_vnf_index))
387
388 if not os.path.exists(config_file):
389 self._log.info("Could not find VNF initial config in archive: %s", config_file)
390 return
391
392 input_data = self.read_from_file(config_file)
393 self._log.info("Loaded VNF config file {}: {}".format(config_file, input_data))
394
395 self.converter.merge_vnfd_config(vnfd, input_data)
396
397 self.converter.add_vnfd_initial_config(
398 vnfd.vnf_configuration.initial_config_primitive,
399 input_data,
400 )
401
402 def read_from_file(self, filename):
403 with open(filename) as fh:
404 input_data = yaml.load(fh)
405 return input_data
406
407 def merge_nsd_config(self, nsd, project_name):
408 nsd_archive = os.path.join(
409 os.getenv('RIFT_VAR_ROOT'),
410 "launchpad/packages/nsd/",
411 project_name,
412 nsd.id,
413 "ns_config")
414
415 self._log.info("Looking for config from the archive {}".format(nsd_archive))
416
417 if not os.path.exists(nsd_archive):
418 return
419
420 config_file = os.path.join(nsd_archive,
421 "{}.yaml".format(nsd.id))
422 if not os.path.exists(config_file):
423 self._log.info("Could not find NS config in archive: %s", config_file)
424 return
425
426 input_data = self.read_from_file(config_file)
427 self._log.info("Loaded NS config file {}: {}".format(config_file, input_data))
428
429 self.converter.merge_nsd_config(nsd, input_data)
430
431 self.converter.merge_nsd_initial_config(nsd, input_data)