RIFT OSM R1 Initial Submission
[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 NsdYang
24 from gi.repository import 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.InitialConfigPrimitive(seq=seq, name=name)
121
122
123 class NsInitialConfigPrimitiveReader(InitialConfigPrimitiveReader):
124 '''Class to read the NS initial config primitives'''
125
126 def __init__(self, primitive_input):
127 super(NsInitialConfigPrimitiveReader, self).__init__(primitive_input)
128
129 def get_initial_config_primitive(self, seq, name):
130 return NsdYang.NsdInitialConfigPrimitive(seq=seq, name=name)
131
132
133 class ConfigPrimitiveConvertor(object):
134 PARAMETER = "parameter"
135 PARAMETER_GROUP = "parameter_group"
136 CONFIG_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.CONFIG_PRIMITIVE] = {}
184 for config_primitive in config_primitives:
185 input_data[self.CONFIG_PRIMITIVE][config_primitive.name] = {}
186 self._extract_parameters(
187 config_primitive.parameter,
188 input_data[self.CONFIG_PRIMITIVE][config_primitive.name])
189
190 try:
191 self._extract_parameter_group(
192 config_primitive.parameter_group,
193 input_data[self.CONFIG_PRIMITIVE][config_primitive.name])
194 except AttributeError:
195 pass
196
197 if not input_data[self.CONFIG_PRIMITIVE][config_primitive.name]:
198 del input_data[self.CONFIG_PRIMITIVE][config_primitive.name]
199
200 if not input_data[self.CONFIG_PRIMITIVE]:
201 del input_data[self.CONFIG_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_config_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.service_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 config 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 = NsInitialConfigPrimitiveReader(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 config_primitive in nsd.initial_config_primitive:
295 for cfg in input_data[self.INITIAL_CONFIG_PRIMITIVE]:
296 if cfg['name'] == config_primitive.name:
297 self.merge_params(
298 config_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-config-primitive for NSD {}: {}".
305 format(nsd.name, e))
306
307
308 def merge_nsd_config(self, nsd, input_data):
309 for config_primitive in nsd.service_primitive:
310 try:
311 cfg = input_data[self.CONFIG_PRIMITIVE][config_primitive.name]
312 except KeyError:
313 continue
314
315 self.merge_params(
316 config_primitive.parameter,
317 cfg[self.PARAMETER])
318
319 for param_group in config_primitive.parameter_group:
320 self.merge_params(
321 param_group.parameter,
322 cfg[self.PARAMETER_GROUP][param_group.name])
323
324 def add_vnfd_initial_config(self, vnfd_init_cfg_prim_msg, input_data):
325 """ Add initial config primitives from VNF Initial Config Input Data
326
327 Arguments:
328 vnfd_init_cfg_prim_msg - manotypes:vnf-configuration/initial_config_primitive pb msg
329 vnf_input_data - VnfInitialConfigPrimitiveReader documented input data
330
331 Raises:
332 InitialConfigReadError: VNF input data was malformed
333 """
334 if self.INITIAL_CONFIG_PRIMITIVE in input_data:
335 vnf_input_data = input_data[self.INITIAL_CONFIG_PRIMITIVE]
336
337 reader = VnfInitialConfigPrimitiveReader(vnf_input_data)
338 for prim in reader.primitives:
339 vnfd_init_cfg_prim_msg.append(prim)
340
341 def merge_vnfd_config(self, vnfd, input_data):
342 for config_primitive in vnfd.vnf_configuration.service_primitive:
343 try:
344 cfg = input_data[self.CONFIG_PRIMITIVE][config_primitive.name]
345 except KeyError:
346 continue
347
348 self.merge_params(
349 config_primitive.parameter,
350 cfg[self.PARAMETER])
351
352
353 class ConfigStore(object):
354 """Convenience class that fetches all the instance related data from the
355 $RIFT_ARTIFACTS/launchpad/libs directory.
356 """
357
358 def __init__(self, log):
359 """
360 Args:
361 log : Log handle.
362 """
363 self._log = log
364 self.converter = ConfigPrimitiveConvertor()
365
366 def merge_vnfd_config(self, nsd_id, vnfd, member_vnf_index):
367 """Merges the vnfd config from the config directory.
368
369 Args:
370 nsd_id (str): Id of the NSD object
371 vnfd_msg : VNFD pb message containing the VNFD id and
372 the member index ref.
373 """
374 nsd_archive = os.path.join(
375 os.getenv('RIFT_ARTIFACTS'),
376 "launchpad/libs",
377 nsd_id,
378 "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):
408 nsd_archive = os.path.join(
409 os.getenv('RIFT_ARTIFACTS'),
410 "launchpad/libs",
411 nsd.id,
412 "config")
413
414 self._log.info("Looking for config from the archive {}".format(nsd_archive))
415
416 if not os.path.exists(nsd_archive):
417 return
418
419 config_file = os.path.join(nsd_archive,
420 "{}.yaml".format(nsd.id))
421 if not os.path.exists(config_file):
422 self._log.info("Could not find NS config in archive: %s", config_file)
423 return
424
425 input_data = self.read_from_file(config_file)
426 self._log.info("Loaded NS config file {}: {}".format(config_file, input_data))
427
428 self.converter.merge_nsd_config(nsd, input_data)
429
430 self.converter.merge_nsd_initial_config(nsd, input_data)