RIFT OSM R1 Initial Submission
[osm/SO.git] / rwcm / plugins / rwconman / rift / tasklets / rwconmantasklet / rwconman_config.py
1
2 #
3 # Copyright 2016 RIFT.IO Inc
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 #
17
18 import asyncio
19 import os
20 import stat
21 import subprocess
22 import sys
23 import tempfile
24 import yaml
25
26 from gi.repository import (
27 RwDts as rwdts,
28 RwConmanYang as conmanY,
29 ProtobufC,
30 )
31
32 import rift.tasklets
33
34 from . import rwconman_conagent as conagent
35 from . import RiftCM_rpc
36 from . import riftcm_config_plugin
37
38 if sys.version_info < (3, 4, 4):
39 asyncio.ensure_future = asyncio.async
40
41 def get_vnf_unique_name(nsr_name, vnfr_short_name, member_vnf_index):
42 return "{}.{}.{}".format(nsr_name, vnfr_short_name, member_vnf_index)
43
44 class ConmanConfigError(Exception):
45 pass
46
47
48 class InitialConfigError(ConmanConfigError):
49 pass
50
51
52 def log_this_vnf(vnf_cfg):
53 log_vnf = ""
54 used_item_list = ['nsr_name', 'vnfr_name', 'member_vnf_index', 'mgmt_ip_address']
55 for item in used_item_list:
56 if item in vnf_cfg:
57 if item == 'mgmt_ip_address':
58 log_vnf += "({})".format(vnf_cfg[item])
59 else:
60 log_vnf += "{}/".format(vnf_cfg[item])
61 return log_vnf
62
63 class PretendNsm(object):
64 def __init__(self, dts, log, loop, parent):
65 self._dts = dts
66 self._log = log
67 self._loop = loop
68 self._parent = parent
69 self._nsrs = {}
70 self._nsr_dict = parent._nsr_dict
71 self._config_agent_plugins = []
72 self._nsd_msg = {}
73
74 @property
75 def nsrs(self):
76 # Expensive, instead use get_nsr, if you know id.
77 self._nsrs = {}
78 # Update the list of nsrs (agent nsr)
79 for id, nsr_obj in self._nsr_dict.items():
80 self._nsrs[id] = nsr_obj.agent_nsr
81 return self._nsrs
82
83 def get_nsr(self, nsr_id):
84 if nsr_id in self._nsr_dict:
85 nsr_obj = self._nsr_dict[nsr_id]
86 return nsr_obj._nsr
87 return None
88
89 def get_vnfr_msg(self, vnfr_id, nsr_id=None):
90 self._log.debug("get_vnfr_msg(vnfr=%s, nsr=%s)",
91 vnfr_id, nsr_id)
92 found = False
93 if nsr_id:
94 if nsr_id in self._nsr_dict:
95 nsr_obj = self._nsr_dict[nsr_id]
96 if vnfr_id in nsr_obj._vnfr_dict:
97 found = True
98 else:
99 for nsr_obj in self._nsr_dict.values():
100 if vnfr_id in nsr_obj._vnfr_dict:
101 # Found it
102 found = True
103 break
104 if found:
105 vnf_cfg = nsr_obj._vnfr_dict[vnfr_id]['vnf_cfg']
106 return vnf_cfg['agent_vnfr'].vnfr_msg
107 else:
108 return None
109
110 @asyncio.coroutine
111 def get_nsd(self, nsr_id):
112 if nsr_id not in self._nsd_msg:
113 nsr_config = yield from self._parent.cmdts_obj.get_nsr_config(nsr_id)
114 self._nsd_msg[nsr_id] = nsr_config.nsd
115 return self._nsd_msg[nsr_id]
116
117 @property
118 def config_agent_plugins(self):
119 self._config_agent_plugins = []
120 for agent in self._parent._config_agent_mgr._plugin_instances.values():
121 self._config_agent_plugins.append(agent)
122 return self._config_agent_plugins
123
124 class ConfigManagerConfig(object):
125 def __init__(self, dts, log, loop, parent):
126 self._dts = dts
127 self._log = log
128 self._loop = loop
129 self._parent = parent
130 self._nsr_dict = {}
131 self.pending_cfg = {}
132 self.terminate_cfg = {}
133 self.pending_tasks = [] # User for NSRid get retry
134 # (mainly excercised at restart case)
135 self._config_xpath = "C,/cm-config"
136 self._opdata_xpath = "D,/rw-conman:cm-state"
137
138 self.cm_config = conmanY.SoConfig()
139 # RO specific configuration
140 self.ro_config = {}
141 for key in self.cm_config.ro_endpoint.fields:
142 self.ro_config[key] = None
143
144 # Initialize cm-state
145 self.cm_state = {}
146 self.cm_state['cm_nsr'] = []
147 self.cm_state['states'] = "Initialized"
148
149 # Initialize objects to register
150 self.cmdts_obj = ConfigManagerDTS(self._log, self._loop, self, self._dts)
151 self._config_agent_mgr = conagent.RiftCMConfigAgent(
152 self._dts,
153 self._log,
154 self._loop,
155 self,
156 )
157 self.reg_handles = [
158 self.cmdts_obj,
159 self._config_agent_mgr,
160 RiftCM_rpc.RiftCMRPCHandler(self._dts, self._log, self._loop,
161 PretendNsm(
162 self._dts, self._log, self._loop, self)),
163 ]
164
165 def is_nsr_valid(self, nsr_id):
166 if nsr_id in self._nsr_dict:
167 return True
168 return False
169
170 def add_to_pending_tasks(self, task):
171 if self.pending_tasks:
172 for p_task in self.pending_tasks:
173 if p_task['nsrid'] == task['nsrid']:
174 # Already queued
175 return
176 try:
177 self.pending_tasks.append(task)
178 self._log.debug("add_to_pending_tasks (nsrid:%s)",
179 task['nsrid'])
180 if len(self.pending_tasks) == 1:
181 self._loop.create_task(self.ConfigManagerConfig_pending_loop())
182 # TBD - change to info level
183 self._log.debug("Started pending_loop!")
184 except Exception as e:
185 self._log.error("Failed adding to pending tasks (%s)", str(e))
186
187 def del_from_pending_tasks(self, task):
188 try:
189 self.pending_tasks.remove(task)
190 except Exception as e:
191 self._log.error("Failed removing from pending tasks (%s)", str(e))
192
193 @asyncio.coroutine
194 def ConfigManagerConfig_pending_loop(self):
195 loop_sleep = 2
196 while True:
197 yield from asyncio.sleep(loop_sleep, loop=self._loop)
198 """
199 This pending task queue is ordred by events,
200 must finish previous task successfully to be able to go on to the next task
201 """
202 if self.pending_tasks:
203 self._log.debug("self.pending_tasks len=%s", len(self.pending_tasks))
204 task = self.pending_tasks[0]
205 done = False
206 if 'nsrid' in task:
207 nsrid = task['nsrid']
208 self._log.debug("Will execute pending task for NSR id(%s)", nsrid)
209 try:
210 # Try to configure this NSR
211 task['retries'] -= 1
212 done = yield from self.config_NSR(nsrid)
213 self._log.info("self.config_NSR status=%s", done)
214
215 except Exception as e:
216 self._log.error("Failed(%s) configuring NSR(%s)," \
217 "retries remained:%d!",
218 str(e), nsrid, task['retries'])
219 finally:
220 self.pending_tasks.remove(task)
221
222 if done:
223 self._log.debug("Finished pending task NSR id(%s):", nsrid)
224 else:
225 self._log.error("Failed configuring NSR(%s), retries remained:%d!",
226 nsrid, task['retries'])
227
228 # Failed, re-insert (append at the end)
229 # this failed task to be retried later
230 # If any retries remained.
231 if task['retries']:
232 self.pending_tasks.append(task)
233 else:
234 self._log.debug("Stopped pending_loop!")
235 break
236
237 @asyncio.coroutine
238 def register(self):
239 yield from self.register_cm_state_opdata()
240
241 # Initialize all handles that needs to be registered
242 for reg in self.reg_handles:
243 yield from reg.register()
244
245 @asyncio.coroutine
246 def register_cm_state_opdata(self):
247
248 def state_to_string(state):
249 state_dict = {
250 conmanY.RecordState.INIT : "init",
251 conmanY.RecordState.RECEIVED : "received",
252 conmanY.RecordState.CFG_PROCESS : "cfg_process",
253 conmanY.RecordState.CFG_PROCESS_FAILED : "cfg_process_failed",
254 conmanY.RecordState.CFG_SCHED : "cfg_sched",
255 conmanY.RecordState.CFG_DELAY : "cfg_delay",
256 conmanY.RecordState.CONNECTING : "connecting",
257 conmanY.RecordState.FAILED_CONNECTION : "failed_connection",
258 conmanY.RecordState.NETCONF_CONNECTED : "netconf_connected",
259 conmanY.RecordState.NETCONF_SSH_CONNECTED : "netconf_ssh_connected",
260 conmanY.RecordState.RESTCONF_CONNECTED : "restconf_connected",
261 conmanY.RecordState.CFG_SEND : "cfg_send",
262 conmanY.RecordState.CFG_FAILED : "cfg_failed",
263 conmanY.RecordState.READY_NO_CFG : "ready_no_cfg",
264 conmanY.RecordState.READY : "ready",
265 }
266 return state_dict[state]
267
268 @asyncio.coroutine
269 def on_prepare(xact_info, action, ks_path, msg):
270
271 self._log.debug("Received cm-state: msg=%s, action=%s", msg, action)
272
273 if action == rwdts.QueryAction.READ:
274 show_output = conmanY.CmOpdata()
275 show_output.from_dict(self.cm_state)
276 self._log.debug("Responding to SHOW cm-state: %s", self.cm_state)
277 xact_info.respond_xpath(rwdts.XactRspCode.ACK,
278 xpath=self._opdata_xpath,
279 msg=show_output)
280 else:
281 xact_info.respond_xpath(rwdts.XactRspCode.ACK)
282
283 self._log.info("Registering for cm-opdata xpath: %s",
284 self._opdata_xpath)
285
286 try:
287 handler=rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare)
288 yield from self._dts.register(xpath=self._opdata_xpath,
289 handler=handler,
290 flags=rwdts.Flag.PUBLISHER)
291 self._log.info("Successfully registered for opdata(%s)", self._opdata_xpath)
292 except Exception as e:
293 self._log.error("Failed to register for opdata as (%s)", e)
294
295 @asyncio.coroutine
296 def process_nsd_vnf_configuration(self, nsr_obj, vnfr):
297
298 def get_config_method(vnf_config):
299 cfg_types = ['netconf', 'juju', 'script']
300 for method in cfg_types:
301 if method in vnf_config:
302 return method
303 return None
304
305 def get_cfg_file_extension(method, configuration_options):
306 ext_dict = {
307 "netconf" : "xml",
308 "script" : {
309 "bash" : "sh",
310 "expect" : "exp",
311 },
312 "juju" : "yml"
313 }
314
315 if method == "netconf":
316 return ext_dict[method]
317 elif method == "script":
318 return ext_dict[method][configuration_options['script_type']]
319 elif method == "juju":
320 return ext_dict[method]
321 else:
322 return "cfg"
323
324 # This is how the YAML file should look like,
325 # This routine will be called for each VNF, so keep appending the file.
326 # priority order is determined by the number,
327 # hence no need to generate the file in that order. A dictionary will be
328 # used that will take care of the order by number.
329 '''
330 1 : <== This is priority
331 name : trafsink_vnfd
332 member_vnf_index : 2
333 configuration_delay : 120
334 configuration_type : netconf
335 configuration_options :
336 username : admin
337 password : admin
338 port : 2022
339 target : running
340 2 :
341 name : trafgen_vnfd
342 member_vnf_index : 1
343 configuration_delay : 0
344 configuration_type : netconf
345 configuration_options :
346 username : admin
347 password : admin
348 port : 2022
349 target : running
350 '''
351
352 # Save some parameters needed as short cuts in flat structure (Also generated)
353 vnf_cfg = vnfr['vnf_cfg']
354 # Prepare unique name for this VNF
355 vnf_cfg['vnf_unique_name'] = get_vnf_unique_name(
356 vnf_cfg['nsr_name'], vnfr['short_name'], vnfr['member_vnf_index_ref'])
357
358 nsr_obj.cfg_path_prefix = '{}/{}_{}'.format(
359 nsr_obj.this_nsr_dir, vnfr['short_name'], vnfr['member_vnf_index_ref'])
360 nsr_vnfr = '{}/{}_{}'.format(
361 vnf_cfg['nsr_name'], vnfr['short_name'], vnfr['member_vnf_index_ref'])
362
363 # Get vnf_configuration from vnfr
364 vnf_config = vnfr['vnf_configuration']
365
366 self._log.debug("vnf_configuration = %s", vnf_config)
367
368 # Create priority dictionary
369 cfg_priority_order = 0
370 if ('config_attributes' in vnf_config and
371 'config_priority' in vnf_config['config_attributes']):
372 cfg_priority_order = vnf_config['config_attributes']['config_priority']
373
374 if cfg_priority_order not in nsr_obj.nsr_cfg_config_attributes_dict:
375 # No VNFR with this priority yet, initialize the list
376 nsr_obj.nsr_cfg_config_attributes_dict[cfg_priority_order] = []
377
378 method = get_config_method(vnf_config)
379 if method is not None:
380 # Create all sub dictionaries first
381 config_priority = {
382 'id' : vnfr['id'],
383 'name' : vnfr['short_name'],
384 'member_vnf_index' : vnfr['member_vnf_index_ref'],
385 }
386
387 if 'config_delay' in vnf_config['config_attributes']:
388 config_priority['configuration_delay'] = vnf_config['config_attributes']['config_delay']
389 vnf_cfg['config_delay'] = config_priority['configuration_delay']
390
391 configuration_options = {}
392 self._log.debug("config method=%s", method)
393 config_priority['configuration_type'] = method
394 vnf_cfg['config_method'] = method
395
396 # Set config agent based on method
397 self._config_agent_mgr.set_config_agent(
398 nsr_obj.agent_nsr, vnf_cfg['agent_vnfr'], method)
399
400 cfg_opt_list = [
401 'port', 'target', 'script_type', 'ip_address', 'user', 'secret',
402 ]
403 for cfg_opt in cfg_opt_list:
404 if cfg_opt in vnf_config[method]:
405 configuration_options[cfg_opt] = vnf_config[method][cfg_opt]
406 vnf_cfg[cfg_opt] = configuration_options[cfg_opt]
407
408 cfg_opt_list = ['mgmt_ip_address', 'username', 'password']
409 for cfg_opt in cfg_opt_list:
410 if cfg_opt in vnf_config['config_access']:
411 configuration_options[cfg_opt] = vnf_config['config_access'][cfg_opt]
412 vnf_cfg[cfg_opt] = configuration_options[cfg_opt]
413
414 # Add to the cp_dict
415 vnf_cp_dict = nsr_obj._cp_dict[vnfr['member_vnf_index_ref']]
416 vnf_cp_dict['rw_mgmt_ip'] = vnf_cfg['mgmt_ip_address']
417 vnf_cp_dict['rw_username'] = vnf_cfg['username']
418 vnf_cp_dict['rw_password'] = vnf_cfg['password']
419
420
421 # TBD - see if we can neatly include the config in "config_attributes" file, no need though
422 #config_priority['config_template'] = vnf_config['config_template']
423 # Create config file
424 vnf_cfg['juju_script'] = os.path.join(self._parent.cfg_dir, 'juju_if.py')
425
426 if 'config_template' in vnf_config:
427 vnf_cfg['cfg_template'] = '{}_{}_template.cfg'.format(nsr_obj.cfg_path_prefix, config_priority['configuration_type'])
428 vnf_cfg['cfg_file'] = '{}.{}'.format(nsr_obj.cfg_path_prefix, get_cfg_file_extension(method, configuration_options))
429 vnf_cfg['xlate_script'] = os.path.join(self._parent.cfg_dir, 'xlate_cfg.py')
430 try:
431 # Now write this template into file
432 with open(vnf_cfg['cfg_template'], "w") as cf:
433 cf.write(vnf_config['config_template'])
434 except Exception as e:
435 self._log.error("Processing NSD, failed to generate configuration template : %s (Error : %s)",
436 vnf_config['config_template'], str(e))
437 raise
438
439 self._log.debug("VNF endpoint so far: %s", vnf_cfg)
440
441 # Populate filled up dictionary
442 config_priority['configuration_options'] = configuration_options
443 nsr_obj.nsr_cfg_config_attributes_dict[cfg_priority_order].append(config_priority)
444 nsr_obj.num_vnfs_to_cfg += 1
445 nsr_obj._vnfr_dict[vnf_cfg['vnf_unique_name']] = vnfr
446 nsr_obj._vnfr_dict[vnfr['id']] = vnfr
447
448 self._log.debug("VNF:(%s) config_attributes = %s",
449 log_this_vnf(vnfr['vnf_cfg']),
450 nsr_obj.nsr_cfg_config_attributes_dict)
451 else:
452 self._log.info("VNF:(%s) is not to be configured by Configuration Manager!",
453 log_this_vnf(vnfr['vnf_cfg']))
454 yield from nsr_obj.update_vnf_cm_state(vnfr, conmanY.RecordState.READY_NO_CFG)
455
456 # Update the cm-state
457 nsr_obj.populate_vm_state_from_vnf_cfg()
458
459 @asyncio.coroutine
460 def config_NSR(self, id):
461
462 def my_yaml_dump(config_attributes_dict, yf):
463
464 yaml_dict = dict(sorted(config_attributes_dict.items()))
465 yf.write(yaml.dump(yaml_dict, default_flow_style=False))
466
467 nsr_dict = self._nsr_dict
468 self._log.info("Configure NSR, id = %s", id)
469
470 #####################TBD###########################
471 # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_create_nsr', self.id, self._nsd)
472 # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_nsr_active', self.id, self._vnfrs)
473
474 try:
475 if id not in nsr_dict:
476 nsr_obj = ConfigManagerNSR(self._log, self._loop, self, id)
477 nsr_dict[id] = nsr_obj
478 else:
479 self._log.info("NSR(%s) is already initialized!", id)
480 nsr_obj = nsr_dict[id]
481 except Exception as e:
482 self._log.error("Failed creating NSR object for (%s) as (%s)", id, str(e))
483 raise
484
485 # Try to configure this NSR only if not already processed
486 if nsr_obj.cm_nsr['state'] != nsr_obj.state_to_string(conmanY.RecordState.INIT):
487 self._log.debug("NSR(%s) is already processed, state=%s",
488 nsr_obj.nsr_name, nsr_obj.cm_nsr['state'])
489 yield from nsr_obj.publish_cm_state()
490 return True
491
492 cmdts_obj = self.cmdts_obj
493 try:
494 # Fetch NSR
495 nsr = yield from cmdts_obj.get_nsr(id)
496 self._log.debug("Full NSR : %s", nsr)
497 if nsr['operational_status'] != "running":
498 self._log.info("NSR(%s) is not ready yet!", nsr['nsd_name_ref'])
499 return False
500 self._nsr = nsr
501
502 # Create Agent NSR class
503 nsr_config = yield from cmdts_obj.get_nsr_config(id)
504 self._log.debug("NSR {} config: {}".format(id, nsr_config))
505 nsr_obj.agent_nsr = riftcm_config_plugin.RiftCMnsr(nsr, nsr_config)
506
507 try:
508 yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.RECEIVED)
509
510 # Parse NSR
511 if nsr is not None:
512 nsr_obj.set_nsr_name(nsr['nsd_name_ref'])
513 nsr_dir = os.path.join(self._parent.cfg_dir, nsr_obj.nsr_name)
514 self._log.info("Checking NS config directory: %s", nsr_dir)
515 if not os.path.isdir(nsr_dir):
516 os.makedirs(nsr_dir)
517 # self._log.critical("NS %s is not to be configured by Service Orchestrator!", nsr_obj.nsr_name)
518 # yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.READY_NO_CFG)
519 # return
520
521 nsr_obj.set_config_dir(self)
522
523 for const_vnfr in nsr['constituent_vnfr_ref']:
524 self._log.debug("Fetching VNFR (%s)", const_vnfr['vnfr_id'])
525 vnfr_msg = yield from cmdts_obj.get_vnfr(const_vnfr['vnfr_id'])
526 if vnfr_msg:
527 vnfr = vnfr_msg.as_dict()
528 self._log.info("create VNF:{}/{}".format(nsr_obj.nsr_name, vnfr['short_name']))
529 agent_vnfr = yield from nsr_obj.add_vnfr(vnfr, vnfr_msg)
530
531 # Preserve order, self.process_nsd_vnf_configuration()
532 # sets up the config agent based on the method
533 yield from self.process_nsd_vnf_configuration(nsr_obj, vnfr)
534 yield from self._config_agent_mgr.invoke_config_agent_plugins(
535 'notify_create_vnfr',
536 nsr_obj.agent_nsr,
537 agent_vnfr)
538
539 #####################TBD###########################
540 # self._log.debug("VNF active. Apply initial config for vnfr {}".format(vnfr.name))
541 # yield from self._config_agent_mgr.invoke_config_agent_plugins('apply_initial_config',
542 # vnfr.id, vnfr)
543 # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_terminate_vnf', self.id, vnfr)
544
545 except Exception as e:
546 self._log.error("Failed processing NSR (%s) as (%s)", nsr_obj.nsr_name, str(e))
547 self._log.exception(e)
548 yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.CFG_PROCESS_FAILED)
549 raise
550
551 try:
552 # Generate config_config_attributes.yaml (For debug reference)
553 with open(nsr_obj.config_attributes_file, "w") as yf:
554 my_yaml_dump(nsr_obj.nsr_cfg_config_attributes_dict, yf)
555 except Exception as e:
556 self._log.error("NS:(%s) failed to write config attributes file as (%s)", nsr_obj.nsr_name, str(e))
557
558 try:
559 # Generate nsr_xlate_dict.yaml (For debug reference)
560 with open(nsr_obj.xlate_dict_file, "w") as yf:
561 yf.write(yaml.dump(nsr_obj._cp_dict, default_flow_style=False))
562 except Exception as e:
563 self._log.error("NS:(%s) failed to write nsr xlate tags file as (%s)", nsr_obj.nsr_name, str(e))
564
565 self._log.debug("Starting to configure each VNF")
566
567 # Check if this NS has input parametrs
568 self._log.info("Checking NS configuration order: %s", nsr_obj.config_attributes_file)
569
570 if os.path.exists(nsr_obj.config_attributes_file):
571 # Apply configuration is specified order
572 try:
573 # Go in loop to configure by specified order
574 self._log.info("Using Dynamic configuration input parametrs for NS: %s", nsr_obj.nsr_name)
575
576 # cfg_delay = nsr_obj.nsr_cfg_config_attributes_dict['configuration_delay']
577 # if cfg_delay:
578 # self._log.info("Applying configuration delay for NS (%s) ; %d seconds",
579 # nsr_obj.nsr_name, cfg_delay)
580 # yield from asyncio.sleep(cfg_delay, loop=self._loop)
581
582 for config_attributes_dict in nsr_obj.nsr_cfg_config_attributes_dict.values():
583 # Iterate through each priority level
584 for vnf_config_attributes_dict in config_attributes_dict:
585 # Iterate through each vnfr at this priority level
586
587 # Make up vnf_unique_name with vnfd name and member index
588 #vnfr_name = "{}.{}".format(nsr_obj.nsr_name, vnf_config_attributes_dict['name'])
589 vnf_unique_name = get_vnf_unique_name(
590 nsr_obj.nsr_name,
591 vnf_config_attributes_dict['name'],
592 str(vnf_config_attributes_dict['member_vnf_index']),
593 )
594 self._log.info("NS (%s) : VNF (%s) - Processing configuration attributes",
595 nsr_obj.nsr_name, vnf_unique_name)
596
597 # Find vnfr for this vnf_unique_name
598 if vnf_unique_name not in nsr_obj._vnfr_dict:
599 self._log.error("NS (%s) - Can not find VNF to be configured: %s", nsr_obj.nsr_name, vnf_unique_name)
600 else:
601 # Save this unique VNF's config input parameters
602 nsr_obj.vnf_config_attributes_dict[vnf_unique_name] = vnf_config_attributes_dict
603 nsr_obj.ConfigVNF(nsr_obj._vnfr_dict[vnf_unique_name])
604
605 # Now add the entire NS to the pending config list.
606 self._log.info("Scheduling NSR:{} configuration".format(nsr_obj.nsr_name))
607 self._parent.add_to_pending(nsr_obj)
608 self._parent.add_nsr_obj(nsr_obj)
609
610 except Exception as e:
611 self._log.error("Failed processing input parameters for NS (%s) as %s", nsr_obj.nsr_name, str(e))
612 raise
613 else:
614 self._log.error("No configuration input parameters for NSR (%s)", nsr_obj.nsr_name)
615
616 except Exception as e:
617 self._log.error("Failed to configure NS (%s) as (%s)", nsr_obj.nsr_name, str(e))
618 yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.CFG_PROCESS_FAILED)
619 raise
620
621 return True
622
623 @asyncio.coroutine
624 def terminate_NSR(self, id):
625 nsr_dict = self._nsr_dict
626 if id not in nsr_dict:
627 self._log.error("NSR(%s) does not exist!", id)
628 return
629 else:
630 # Remove this NSR if we have it on pending task list
631 for task in self.pending_tasks:
632 if task['nsrid'] == id:
633 self.del_from_pending_tasks(task)
634
635 # Remove this object from global list
636 nsr_obj = nsr_dict.pop(id, None)
637
638 # Remove this NS cm-state from global status list
639 self.cm_state['cm_nsr'].remove(nsr_obj.cm_nsr)
640
641 # Also remove any scheduled configuration event
642 for nsr_obj_p in self._parent.pending_cfg:
643 if nsr_obj_p == nsr_obj:
644 assert id == nsr_obj_p._nsr_id
645 #self._parent.pending_cfg.remove(nsr_obj_p)
646 # Mark this as being deleted so we do not try to configure it if we are in cfg_delay (will wake up and continue to process otherwise)
647 nsr_obj_p.being_deleted = True
648 self._log.info("Removed scheduled configuration for NSR(%s)", nsr_obj.nsr_name)
649
650 self._parent.remove_nsr_obj(id)
651
652 # Call Config Agent to clean up for each VNF
653 for agent_vnfr in nsr_obj.agent_nsr.vnfrs:
654 yield from self._config_agent_mgr.invoke_config_agent_plugins(
655 'notify_terminate_vnfr',
656 nsr_obj.agent_nsr,
657 agent_vnfr)
658
659 # publish delete cm-state (cm-nsr)
660 yield from nsr_obj.delete_cm_nsr()
661
662 #####################TBD###########################
663 # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_terminate_ns', self.id)
664
665 self._log.info("NSR(%s/%s) is deleted", nsr_obj.nsr_name, id)
666
667 @asyncio.coroutine
668 def process_ns_initial_config(self, nsr_obj):
669 '''Apply the initial-config-primitives specified in NSD'''
670
671 def get_input_file(parameters):
672 inp = {}
673
674 # Add NSR name to file
675 inp['nsr_name'] = nsr_obj.nsr_name
676
677 # TODO (pjoseph): Add config agents, we need to identify which all
678 # config agents are required from this NS and provide only those
679 inp['config-agent'] = {}
680
681 # Add parameters for initial config
682 inp['parameter'] = {}
683 for parameter in parameters:
684 try:
685 inp['parameter'][parameter['name']] = parameter['value']
686 except KeyError as e:
687 self._log.info("NSR {} initial config parameter {} with no value: {}".
688 format(nsr_obj.nsr_name, parameter, e))
689
690
691 # Add vnfrs specific data
692 inp['vnfr'] = {}
693 for vnfr in nsr_obj.vnfrs:
694 v = {}
695
696 v['name'] = vnfr['name']
697 v['mgmt_ip_address'] = vnfr['vnf_cfg']['mgmt_ip_address']
698 v['mgmt_port'] = vnfr['vnf_cfg']['port']
699
700 if 'dashboard_url' in vnfr:
701 v['dashboard_url'] = vnfr['dashboard_url']
702
703 if 'connection_point' in vnfr:
704 v['connection_point'] = []
705 for cp in vnfr['connection_point']:
706 v['connection_point'].append(
707 {
708 'name': cp['name'],
709 'ip_address': cp['ip_address'],
710 }
711 )
712
713 v['vdur'] = []
714 vdu_data = [(vdu['name'], vdu['management_ip'], vdu['vm_management_ip'], vdu['id'])
715 for vdu in vnfr['vdur']]
716
717 for data in vdu_data:
718 data = dict(zip(['name', 'management_ip', 'vm_management_ip', 'id'] , data))
719 v['vdur'].append(data)
720
721 inp['vnfr'][vnfr['member_vnf_index_ref']] = v
722
723 self._log.debug("Input data for NSR {}: {}".
724 format(nsr_obj.nsr_name, inp))
725
726 # Convert to YAML string
727 yaml_string = yaml.dump(inp, default_flow_style=False)
728
729 # Write the inputs as yaml file
730 tmp_file = None
731 with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
732 tmp_file.write(yaml_string.encode("UTF-8"))
733 self._log.debug("Input file created for NSR {}: {}".
734 format(nsr_obj.nsr_name, tmp_file.name))
735
736 return tmp_file.name
737
738 def get_script_file(script_name, nsd_name, nsd_id):
739 # Get the full path to the script
740 script = ''
741 # If script name starts with /, assume it is full path
742 if script_name[0] == '/':
743 # The script has full path, use as is
744 script = script_name
745 else:
746 script = os.path.join(os.environ['RIFT_ARTIFACTS'],
747 'launchpad/packages/nsd',
748 nsd_id,
749 nsd_name,
750 'scripts',
751 script_name)
752 self._log.debug("Checking for script at %s", script)
753 if not os.path.exists(script):
754 self._log.debug("Did not find script %s", script)
755 script = os.path.join(os.environ['RIFT_INSTALL'],
756 'usr/bin',
757 script_name)
758
759 # Seen cases in jenkins, where the script execution fails
760 # with permission denied. Setting the permission on script
761 # to make sure it has execute permission
762 perm = os.stat(script).st_mode
763 if not (perm & stat.S_IXUSR):
764 self._log.warn("NSR {} initial config script {} " \
765 "without execute permission: {}".
766 format(nsr_id, script, perm))
767 os.chmod(script, perm | stat.S_IXUSR)
768 return script
769
770 nsr_id = nsr_obj.nsr_id
771 nsr_name = nsr_obj.nsr_name
772 self._log.debug("Apply initial config for NSR {}({})".
773 format(nsr_name, nsr_id))
774
775 # Fetch NSR
776 nsr = yield from self.cmdts_obj.get_nsr(nsr_id)
777 if nsr is not None:
778 nsd = yield from self.cmdts_obj.get_nsd(nsr_id)
779
780 try:
781 # Check if initial config is present
782 # TODO (pjoseph): Sort based on seq
783 for conf in nsr['initial_config_primitive']:
784 self._log.debug("Parameter conf: {}".
785 format(conf))
786
787 parameters = []
788 try:
789 parameters = conf['parameter']
790 except Exception as e:
791 self._log.debug("Parameter conf: {}, e: {}".
792 format(conf, e))
793 pass
794
795 inp_file = get_input_file(parameters)
796
797 script = get_script_file(conf['user_defined_script'],
798 nsd.name,
799 nsd.id)
800
801 cmd = "{0} {1}".format(script, inp_file)
802 self._log.debug("Running the CMD: {}".format(cmd))
803
804 process = yield from asyncio. \
805 create_subprocess_shell(cmd, loop=self._loop)
806 yield from process.wait()
807 if process.returncode:
808 msg = "NSR {} initial config using {} failed with {}". \
809 format(nsr_name, script, process.returncode)
810 self._log.error(msg)
811 raise InitialConfigError(msg)
812 else:
813 os.remove(inp_file)
814
815 except KeyError as e:
816 self._log.debug("Did not find initial config {}".
817 format(e))
818
819
820 class ConfigManagerNSR(object):
821 def __init__(self, log, loop, parent, id):
822 self._log = log
823 self._loop = loop
824 self._rwcal = None
825 self._vnfr_dict = {}
826 self._cp_dict = {}
827 self._nsr_id = id
828 self._parent = parent
829 self._log.info("Instantiated NSR entry for id=%s", id)
830 self.nsr_cfg_config_attributes_dict = {}
831 self.vnf_config_attributes_dict = {}
832 self.num_vnfs_to_cfg = 0
833 self._vnfr_list = []
834 self.vnf_cfg_list = []
835 self.this_nsr_dir = None
836 self.being_deleted = False
837 self.dts_obj = self._parent.cmdts_obj
838
839 # Initialize cm-state for this NS
840 self.cm_nsr = {}
841 self.cm_nsr['cm_vnfr'] = []
842 self.cm_nsr['id'] = id
843 self.cm_nsr['state'] = self.state_to_string(conmanY.RecordState.INIT)
844 self.cm_nsr['state_details'] = None
845
846 self.set_nsr_name('Not Set')
847
848 # Add this NSR cm-state object to global cm-state
849 parent.cm_state['cm_nsr'].append(self.cm_nsr)
850
851 # Place holders for NSR & VNFR classes
852 self.agent_nsr = None
853
854 @property
855 def nsr_opdata_xpath(self):
856 ''' Returns full xpath for this NSR cm-state opdata '''
857 return(
858 "D,/rw-conman:cm-state" +
859 "/rw-conman:cm-nsr[rw-conman:id='{}']"
860 ).format(self._nsr_id)
861
862 @property
863 def vnfrs(self):
864 return self._vnfr_list
865
866 @property
867 def parent(self):
868 return self._parent
869
870 @property
871 def nsr_id(self):
872 return self._nsr_id
873
874 @asyncio.coroutine
875 def publish_cm_state(self):
876 ''' This function publishes cm_state for this NSR '''
877
878 cm_state = conmanY.CmOpdata()
879 cm_state_nsr = cm_state.cm_nsr.add()
880 cm_state_nsr.from_dict(self.cm_nsr)
881 #with self._dts.transaction() as xact:
882 yield from self.dts_obj.update(self.nsr_opdata_xpath, cm_state_nsr)
883 self._log.info("Published cm-state with xpath %s and nsr %s",
884 self.nsr_opdata_xpath,
885 cm_state_nsr)
886
887 @asyncio.coroutine
888 def delete_cm_nsr(self):
889 ''' This function publishes cm_state for this NSR '''
890
891 yield from self.dts_obj.delete(self.nsr_opdata_xpath)
892 self._log.info("Deleted cm-nsr with xpath %s",
893 self.nsr_opdata_xpath)
894
895 def set_nsr_name(self, name):
896 self.nsr_name = name
897 self.cm_nsr['name'] = name
898
899 def set_config_dir(self, caller):
900 self.this_nsr_dir = os.path.join(
901 caller._parent.cfg_dir, self.nsr_name, caller._nsr['name_ref'])
902 if not os.path.exists(self.this_nsr_dir):
903 os.makedirs(self.this_nsr_dir)
904 self._log.debug("NSR:(%s), Created configuration directory(%s)",
905 caller._nsr['name_ref'], self.this_nsr_dir)
906 self.config_attributes_file = os.path.join(self.this_nsr_dir, "configuration_config_attributes.yml")
907 self.xlate_dict_file = os.path.join(self.this_nsr_dir, "nsr_xlate_dict.yml")
908
909 def xlate_conf(self, vnfr, vnf_cfg):
910
911 # If configuration type is not already set, try to read from attributes
912 if vnf_cfg['interface_type'] is None:
913 # Prepare unique name for this VNF
914 vnf_unique_name = get_vnf_unique_name(
915 vnf_cfg['nsr_name'],
916 vnfr['short_name'],
917 vnfr['member_vnf_index_ref'],
918 )
919
920 # Find this particular (unique) VNF's config attributes
921 if (vnf_unique_name in self.vnf_config_attributes_dict):
922 vnf_cfg_config_attributes_dict = self.vnf_config_attributes_dict[vnf_unique_name]
923 vnf_cfg['interface_type'] = vnf_cfg_config_attributes_dict['configuration_type']
924 if 'configuration_options' in vnf_cfg_config_attributes_dict:
925 cfg_opts = vnf_cfg_config_attributes_dict['configuration_options']
926 for key, value in cfg_opts.items():
927 vnf_cfg[key] = value
928
929 cfg_path_prefix = '{}/{}/{}_{}'.format(
930 self._parent._parent.cfg_dir,
931 vnf_cfg['nsr_name'],
932 vnfr['short_name'],
933 vnfr['member_vnf_index_ref'],
934 )
935
936 vnf_cfg['cfg_template'] = '{}_{}_template.cfg'.format(cfg_path_prefix, vnf_cfg['interface_type'])
937 vnf_cfg['cfg_file'] = '{}.cfg'.format(cfg_path_prefix)
938 vnf_cfg['xlate_script'] = self._parent._parent.cfg_dir + '/xlate_cfg.py'
939
940 self._log.debug("VNF endpoint so far: %s", vnf_cfg)
941
942 self._log.info("Checking cfg_template %s", vnf_cfg['cfg_template'])
943 if os.path.exists(vnf_cfg['cfg_template']):
944 return True
945 return False
946
947 def ConfigVNF(self, vnfr):
948
949 vnf_cfg = vnfr['vnf_cfg']
950 vnf_cm_state = self.find_or_create_vnfr_cm_state(vnf_cfg)
951
952 if (vnf_cm_state['state'] == self.state_to_string(conmanY.RecordState.READY_NO_CFG)
953 or
954 vnf_cm_state['state'] == self.state_to_string(conmanY.RecordState.READY)):
955 self._log.warning("NS/VNF (%s/%s) is already configured! Skipped.", self.nsr_name, vnfr['short_name'])
956 return
957
958 #UPdate VNF state
959 vnf_cm_state['state'] = self.state_to_string(conmanY.RecordState.CFG_PROCESS)
960
961 # Now translate the configuration for iP addresses
962 try:
963 # Add cp_dict members (TAGS) for this VNF
964 self._cp_dict['rw_mgmt_ip'] = vnf_cfg['mgmt_ip_address']
965 self._cp_dict['rw_username'] = vnf_cfg['username']
966 self._cp_dict['rw_password'] = vnf_cfg['password']
967 ############################################################
968 # TBD - Need to lookup above 3 for a given VNF, not global #
969 # Once we do that no need to dump below file again before #
970 # each VNF configuration translation. #
971 # This will require all existing config templates to be #
972 # changed for above three tags to include member index #
973 ############################################################
974 try:
975 nsr_obj = vnf_cfg['nsr_obj']
976 # Generate config_config_attributes.yaml (For debug reference)
977 with open(nsr_obj.xlate_dict_file, "w") as yf:
978 yf.write(yaml.dump(nsr_obj._cp_dict, default_flow_style=False))
979 except Exception as e:
980 self._log.error("NS:(%s) failed to write nsr xlate tags file as (%s)", nsr_obj.nsr_name, str(e))
981
982 if 'cfg_template' in vnf_cfg:
983 script_cmd = 'python3 {} -i {} -o {} -x "{}"'.format(vnf_cfg['xlate_script'], vnf_cfg['cfg_template'], vnf_cfg['cfg_file'], self.xlate_dict_file)
984 self._log.debug("xlate script command (%s)", script_cmd)
985 #xlate_msg = subprocess.check_output(script_cmd).decode('utf-8')
986 xlate_msg = subprocess.check_output(script_cmd, shell=True).decode('utf-8')
987 self._log.info("xlate script output (%s)", xlate_msg)
988 except Exception as e:
989 vnf_cm_state['state'] = self.state_to_string(conmanY.RecordState.CFG_PROCESS_FAILED)
990 self._log.error("Failed to execute translation script for VNF: %s with (%s)", log_this_vnf(vnf_cfg), str(e))
991 return
992
993 self._log.info("Applying config to VNF: %s = %s!", log_this_vnf(vnf_cfg), vnf_cfg)
994 try:
995 #self.vnf_cfg_list.append(vnf_cfg)
996 self._log.debug("Scheduled configuration!")
997 vnf_cm_state['state'] = self.state_to_string(conmanY.RecordState.CFG_SCHED)
998 except Exception as e:
999 self._log.error("Failed apply_vnf_config to VNF: %s as (%s)", log_this_vnf(vnf_cfg), str(e))
1000 vnf_cm_state['state'] = self.state_to_string(conmanY.RecordState.CFG_PROCESS_FAILED)
1001 raise
1002
1003 def add(self, nsr):
1004 self._log.info("Adding NS Record for id=%s", id)
1005 self._nsr = nsr
1006
1007 def sample_cm_state(self):
1008 return (
1009 {
1010 'cm_nsr': [
1011 {
1012 'cm_vnfr': [
1013 {
1014 'cfg_location': 'location1',
1015 'cfg_type': 'script',
1016 'connection_point': [
1017 {'ip_address': '1.1.1.1', 'name': 'vnf1cp1'},
1018 {'ip_address': '1.1.1.2', 'name': 'vnf1cp2'}
1019 ],
1020 'id': 'vnfrid1',
1021 'mgmt_interface': {'ip_address': '7.1.1.1',
1022 'port': 1001},
1023 'name': 'vnfrname1',
1024 'state': 'init'
1025 },
1026 {
1027 'cfg_location': 'location2',
1028 'cfg_type': 'netconf',
1029 'connection_point': [{'ip_address': '2.1.1.1', 'name': 'vnf2cp1'},
1030 {'ip_address': '2.1.1.2', 'name': 'vnf2cp2'}],
1031 'id': 'vnfrid2',
1032 'mgmt_interface': {'ip_address': '7.1.1.2',
1033 'port': 1001},
1034 'name': 'vnfrname2',
1035 'state': 'init'}
1036 ],
1037 'id': 'nsrid1',
1038 'name': 'nsrname1',
1039 'state': 'init'}
1040 ],
1041 'states': 'Initialized, '
1042 })
1043
1044 def populate_vm_state_from_vnf_cfg(self):
1045 # Fill in each VNFR from this nsr object
1046 vnfr_list = self._vnfr_list
1047 for vnfr in vnfr_list:
1048 vnf_cfg = vnfr['vnf_cfg']
1049 vnf_cm_state = self.find_vnfr_cm_state(vnfr['id'])
1050
1051 if vnf_cm_state:
1052 # Fill in VNF management interface
1053 vnf_cm_state['mgmt_interface']['ip_address'] = vnf_cfg['mgmt_ip_address']
1054 vnf_cm_state['mgmt_interface']['port'] = vnf_cfg['port']
1055
1056 # Fill in VNF configuration details
1057 vnf_cm_state['cfg_type'] = vnf_cfg['config_method']
1058 vnf_cm_state['cfg_location'] = vnf_cfg['cfg_file']
1059
1060 # Fill in each connection-point for this VNF
1061 if "connection_point" in vnfr:
1062 cp_list = vnfr['connection_point']
1063 for cp_item_dict in cp_list:
1064 vnf_cm_state['connection_point'].append(
1065 {
1066 'name' : cp_item_dict['name'],
1067 'ip_address' : cp_item_dict['ip_address'],
1068 }
1069 )
1070
1071 def state_to_string(self, state):
1072 state_dict = {
1073 conmanY.RecordState.INIT : "init",
1074 conmanY.RecordState.RECEIVED : "received",
1075 conmanY.RecordState.CFG_PROCESS : "cfg_process",
1076 conmanY.RecordState.CFG_PROCESS_FAILED : "cfg_process_failed",
1077 conmanY.RecordState.CFG_SCHED : "cfg_sched",
1078 conmanY.RecordState.CFG_DELAY : "cfg_delay",
1079 conmanY.RecordState.CONNECTING : "connecting",
1080 conmanY.RecordState.FAILED_CONNECTION : "failed_connection",
1081 conmanY.RecordState.NETCONF_CONNECTED : "netconf_connected",
1082 conmanY.RecordState.NETCONF_SSH_CONNECTED : "netconf_ssh_connected",
1083 conmanY.RecordState.RESTCONF_CONNECTED : "restconf_connected",
1084 conmanY.RecordState.CFG_SEND : "cfg_send",
1085 conmanY.RecordState.CFG_FAILED : "cfg_failed",
1086 conmanY.RecordState.READY_NO_CFG : "ready_no_cfg",
1087 conmanY.RecordState.READY : "ready",
1088 }
1089 return state_dict[state]
1090
1091 def find_vnfr_cm_state(self, id):
1092 if self.cm_nsr['cm_vnfr']:
1093 for vnf_cm_state in self.cm_nsr['cm_vnfr']:
1094 if vnf_cm_state['id'] == id:
1095 return vnf_cm_state
1096 return None
1097
1098 def find_or_create_vnfr_cm_state(self, vnf_cfg):
1099 vnfr = vnf_cfg['vnfr']
1100 vnf_cm_state = self.find_vnfr_cm_state(vnfr['id'])
1101
1102 if vnf_cm_state is None:
1103 # Not found, Create and Initialize this VNF cm-state
1104 vnf_cm_state = {
1105 'id' : vnfr['id'],
1106 'name' : vnfr['short_name'],
1107 'state' : self.state_to_string(conmanY.RecordState.RECEIVED),
1108 'mgmt_interface' :
1109 {
1110 'ip_address' : vnf_cfg['mgmt_ip_address'],
1111 'port' : vnf_cfg['port'],
1112 },
1113 'cfg_type' : vnf_cfg['config_method'],
1114 'cfg_location' : vnf_cfg['cfg_file'],
1115 'connection_point' : [],
1116 }
1117 self.cm_nsr['cm_vnfr'].append(vnf_cm_state)
1118
1119 # Publish newly created cm-state
1120
1121
1122 return vnf_cm_state
1123
1124 @asyncio.coroutine
1125 def get_vnf_cm_state(self, vnfr):
1126 if vnfr:
1127 vnf_cm_state = self.find_vnfr_cm_state(vnfr['id'])
1128 if vnf_cm_state:
1129 return vnf_cm_state['state']
1130 return False
1131
1132 @asyncio.coroutine
1133 def update_vnf_cm_state(self, vnfr, state):
1134 if vnfr:
1135 vnf_cm_state = self.find_vnfr_cm_state(vnfr['id'])
1136 if vnf_cm_state is None:
1137 self._log.error("No opdata found for NS/VNF:%s/%s!",
1138 self.nsr_name, vnfr['short_name'])
1139 return
1140
1141 if vnf_cm_state['state'] != self.state_to_string(state):
1142 old_state = vnf_cm_state['state']
1143 vnf_cm_state['state'] = self.state_to_string(state)
1144 # Publish new state
1145 yield from self.publish_cm_state()
1146 self._log.info("VNF ({}/{}/{}) state change: {} -> {}"
1147 .format(self.nsr_name,
1148 vnfr['short_name'],
1149 vnfr['member_vnf_index_ref'],
1150 old_state,
1151 vnf_cm_state['state']))
1152
1153 else:
1154 self._log.error("No VNFR supplied for state update (NS=%s)!",
1155 self.nsr_name)
1156
1157 @property
1158 def get_ns_cm_state(self):
1159 return self.cm_nsr['state']
1160
1161 @asyncio.coroutine
1162 def update_ns_cm_state(self, state, state_details=None):
1163 if self.cm_nsr['state'] != self.state_to_string(state):
1164 old_state = self.cm_nsr['state']
1165 self.cm_nsr['state'] = self.state_to_string(state)
1166 self.cm_nsr['state_details'] = state_details if state_details is not None else None
1167 self._log.info("NS ({}) state change: {} -> {}"
1168 .format(self.nsr_name,
1169 old_state,
1170 self.cm_nsr['state']))
1171 # Publish new state
1172 yield from self.publish_cm_state()
1173
1174 @asyncio.coroutine
1175 def add_vnfr(self, vnfr, vnfr_msg):
1176
1177 @asyncio.coroutine
1178 def populate_subnets_from_vlr(id):
1179 try:
1180 # Populate cp_dict with VLR subnet info
1181 vlr = yield from self.dts_obj.get_vlr(id)
1182 if vlr is not None and 'assigned_subnet' in vlr:
1183 subnet = {vlr.name:vlr.assigned_subnet}
1184 self._cp_dict[vnfr['member_vnf_index_ref']].update(subnet)
1185 self._cp_dict.update(subnet)
1186 self._log.debug("VNF:(%s) Updated assigned subnet = %s",
1187 vnfr['short_name'], subnet)
1188 except Exception as e:
1189 self._log.error("VNF:(%s) VLR Error = %s",
1190 vnfr['short_name'], e)
1191
1192 if vnfr['id'] not in self._vnfr_dict:
1193 self._log.info("NSR(%s) : Adding VNF Record for name=%s, id=%s", self._nsr_id, vnfr['short_name'], vnfr['id'])
1194 # Add this vnfr to the list for show, or single traversal
1195 self._vnfr_list.append(vnfr)
1196 else:
1197 self._log.warning("NSR(%s) : VNF Record for name=%s, id=%s already exists, overwriting", self._nsr_id, vnfr['short_name'], vnfr['id'])
1198
1199 # Make vnfr available by id as well as by name
1200 unique_name = get_vnf_unique_name(self.nsr_name, vnfr['short_name'], vnfr['member_vnf_index_ref'])
1201 self._vnfr_dict[unique_name] = vnfr
1202 self._vnfr_dict[vnfr['id']] = vnfr
1203
1204 # Create vnf_cfg dictionary with default values
1205 vnf_cfg = {
1206 'nsr_obj' : self,
1207 'vnfr' : vnfr,
1208 'agent_vnfr' : self.agent_nsr.add_vnfr(vnfr, vnfr_msg),
1209 'nsr_name' : self.nsr_name,
1210 'nsr_id' : self._nsr_id,
1211 'vnfr_name' : vnfr['short_name'],
1212 'member_vnf_index' : vnfr['member_vnf_index_ref'],
1213 'port' : 0,
1214 'username' : 'admin',
1215 'password' : 'admin',
1216 'config_method' : 'None',
1217 'protocol' : 'None',
1218 'mgmt_ip_address' : '0.0.0.0',
1219 'cfg_file' : 'None',
1220 'cfg_retries' : 0,
1221 'script_type' : 'bash',
1222 }
1223
1224 # Update the mgmt ip address
1225 # In case the config method is none, this is not
1226 # updated later
1227 try:
1228 vnf_cfg['mgmt_ip_address'] = vnfr_msg.mgmt_interface.ip_address
1229 vnf_cfg['port'] = vnfr_msg.mgmt_interface.port
1230 except Exception as e:
1231 self._log.warn(
1232 "VNFR {}({}), unable to retrieve mgmt ip address: {}".
1233 format(vnfr['short_name'], vnfr['id'], e))
1234
1235 vnfr['vnf_cfg'] = vnf_cfg
1236 self.find_or_create_vnfr_cm_state(vnf_cfg)
1237
1238 '''
1239 Build the connection-points list for this VNF (self._cp_dict)
1240 '''
1241 # Populate global CP list self._cp_dict from VNFR
1242 cp_list = []
1243 if 'connection_point' in vnfr:
1244 cp_list = vnfr['connection_point']
1245
1246 self._cp_dict[vnfr['member_vnf_index_ref']] = {}
1247 if 'vdur' in vnfr:
1248 for vdur in vnfr['vdur']:
1249 if 'internal_connection_point' in vdur:
1250 cp_list += vdur['internal_connection_point']
1251
1252 for cp_item_dict in cp_list:
1253 # Populate global dictionary
1254 self._cp_dict[
1255 cp_item_dict['name']
1256 ] = cp_item_dict['ip_address']
1257
1258 # Populate unique member specific dictionary
1259 self._cp_dict[
1260 vnfr['member_vnf_index_ref']
1261 ][
1262 cp_item_dict['name']
1263 ] = cp_item_dict['ip_address']
1264
1265 # Fill in the subnets from vlr
1266 if 'vlr_ref' in cp_item_dict:
1267 ### HACK: Internal connection_point do not have VLR reference
1268 yield from populate_subnets_from_vlr(cp_item_dict['vlr_ref'])
1269
1270 if 'internal_vlr' in vnfr:
1271 for ivlr in vnfr['internal_vlr']:
1272 yield from populate_subnets_from_vlr(ivlr['vlr_ref'])
1273
1274 # Update vnfr
1275 vnf_cfg['agent_vnfr']._vnfr = vnfr
1276 return vnf_cfg['agent_vnfr']
1277
1278
1279 class XPaths(object):
1280 @staticmethod
1281 def nsr_opdata(k=None):
1282 return ("D,/nsr:ns-instance-opdata/nsr:nsr" +
1283 ("[nsr:ns-instance-config-ref='{}']".format(k) if k is not None else ""))
1284
1285 @staticmethod
1286 def nsd_msg(k=None):
1287 return ("C,/nsd:nsd-catalog/nsd:nsd" +
1288 "[nsd:id = '{}']".format(k) if k is not None else "")
1289
1290 @staticmethod
1291 def vnfr_opdata(k=None):
1292 return ("D,/vnfr:vnfr-catalog/vnfr:vnfr" +
1293 ("[vnfr:id='{}']".format(k) if k is not None else ""))
1294
1295 @staticmethod
1296 def config_agent(k=None):
1297 return ("D,/rw-config-agent:config-agent/rw-config-agent:account" +
1298 ("[rw-config-agent:name='{}']".format(k) if k is not None else ""))
1299
1300 @staticmethod
1301 def nsr_config(k=None):
1302 return ("C,/nsr:ns-instance-config/nsr:nsr[nsr:id='{}']".format(k) if k is not None else "")
1303
1304 @staticmethod
1305 def vlr(k=None):
1306 return ("D,/vlr:vlr-catalog/vlr:vlr[vlr:id='{}']".format(k) if k is not None else "")
1307
1308 class ConfigManagerDTS(object):
1309 ''' This class either reads from DTS or publishes to DTS '''
1310
1311 def __init__(self, log, loop, parent, dts):
1312 self._log = log
1313 self._loop = loop
1314 self._parent = parent
1315 self._dts = dts
1316
1317 @asyncio.coroutine
1318 def _read_dts(self, xpath, do_trace=False):
1319 self._log.debug("_read_dts path = %s", xpath)
1320 flags = rwdts.XactFlag.MERGE
1321 res_iter = yield from self._dts.query_read(
1322 xpath, flags=flags
1323 )
1324
1325 results = []
1326 try:
1327 for i in res_iter:
1328 result = yield from i
1329 if result is not None:
1330 results.append(result.result)
1331 except:
1332 pass
1333
1334 return results
1335
1336
1337 @asyncio.coroutine
1338 def get_nsr(self, id):
1339 self._log.debug("Attempting to get NSR: %s", id)
1340 nsrl = yield from self._read_dts(XPaths.nsr_opdata(id), False)
1341 nsr = None
1342 if len(nsrl) > 0:
1343 nsr = nsrl[0].as_dict()
1344 return nsr
1345
1346 @asyncio.coroutine
1347 def get_nsr_config(self, id):
1348 self._log.debug("Attempting to get config NSR: %s", id)
1349 nsrl = yield from self._read_dts(XPaths.nsr_config(id), False)
1350 nsr = None
1351 if len(nsrl) > 0:
1352 nsr = nsrl[0]
1353 return nsr
1354
1355 @asyncio.coroutine
1356 def get_nsd_msg(self, id):
1357 self._log.debug("Attempting to get NSD: %s", id)
1358 nsdl = yield from self._read_dts(XPaths.nsd_msg(id), False)
1359 nsd_msg = None
1360 if len(nsdl) > 0:
1361 nsd_msg = nsdl[0]
1362 return nsd_msg
1363
1364 @asyncio.coroutine
1365 def get_nsd(self, nsr_id):
1366 self._log.debug("Attempting to get NSD for NSR: %s", id)
1367 nsr_config = yield from self.get_nsr_config(nsr_id)
1368 nsd_msg = nsr_config.nsd
1369 return nsd_msg
1370
1371 @asyncio.coroutine
1372 def get_vnfr(self, id):
1373 self._log.debug("Attempting to get VNFR: %s", id)
1374 vnfrl = yield from self._read_dts(XPaths.vnfr_opdata(id), do_trace=False)
1375 vnfr_msg = None
1376 if len(vnfrl) > 0:
1377 vnfr_msg = vnfrl[0]
1378 return vnfr_msg
1379
1380 @asyncio.coroutine
1381 def get_vlr(self, id):
1382 self._log.debug("Attempting to get VLR subnet: %s", id)
1383 vlrl = yield from self._read_dts(XPaths.vlr(id), do_trace=True)
1384 vlr_msg = None
1385 if len(vlrl) > 0:
1386 vlr_msg = vlrl[0]
1387 return vlr_msg
1388
1389 @asyncio.coroutine
1390 def get_config_agents(self, name):
1391 self._log.debug("Attempting to get config_agents: %s", name)
1392 cfgagentl = yield from self._read_dts(XPaths.config_agent(name), False)
1393 return cfgagentl
1394
1395 @asyncio.coroutine
1396 def update(self, path, msg, flags=rwdts.XactFlag.REPLACE):
1397 """
1398 Update a cm-state (cm-nsr) record in DTS with the path and message
1399 """
1400 self._log.debug("Updating cm-state %s:%s dts_pub_hdl = %s", path, msg, self.dts_pub_hdl)
1401 self.dts_pub_hdl.update_element(path, msg, flags)
1402 self._log.debug("Updated cm-state, %s:%s", path, msg)
1403
1404 @asyncio.coroutine
1405 def delete(self, path):
1406 """
1407 Delete cm-nsr record in DTS with the path only
1408 """
1409 self._log.debug("Deleting cm-nsr %s dts_pub_hdl = %s", path, self.dts_pub_hdl)
1410 self.dts_pub_hdl.delete_element(path)
1411 self._log.debug("Deleted cm-nsr, %s", path)
1412
1413 @asyncio.coroutine
1414 def register(self):
1415 yield from self.register_to_publish()
1416 yield from self.register_for_nsr()
1417
1418 @asyncio.coroutine
1419 def register_to_publish(self):
1420 ''' Register to DTS for publishing cm-state opdata '''
1421
1422 xpath = "D,/rw-conman:cm-state/rw-conman:cm-nsr"
1423 self._log.debug("Registering to publish cm-state @ %s", xpath)
1424 hdl = rift.tasklets.DTS.RegistrationHandler()
1425 with self._dts.group_create() as group:
1426 self.dts_pub_hdl = group.register(xpath=xpath,
1427 handler=hdl,
1428 flags=rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ)
1429
1430 @property
1431 def nsr_xpath(self):
1432 return "D,/nsr:ns-instance-opdata/nsr:nsr"
1433
1434 @asyncio.coroutine
1435 def register_for_nsr(self):
1436 """ Register for NSR changes """
1437
1438 @asyncio.coroutine
1439 def on_prepare(xact_info, query_action, ks_path, msg):
1440 """ This NSR is created """
1441 self._log.debug("Received NSR instantiate on_prepare (%s:%s:%s)",
1442 query_action,
1443 ks_path,
1444 msg)
1445
1446 if (query_action == rwdts.QueryAction.UPDATE or
1447 query_action == rwdts.QueryAction.CREATE):
1448 msg_dict = msg.as_dict()
1449 # Update Each NSR/VNFR state)
1450 if ('operational_status' in msg_dict and
1451 msg_dict['operational_status'] == 'running'):
1452 # Add to the task list
1453 self._parent.add_to_pending_tasks({'nsrid' : msg_dict['ns_instance_config_ref'], 'retries' : 5})
1454 elif query_action == rwdts.QueryAction.DELETE:
1455 nsr_id = msg.ns_instance_config_ref
1456 asyncio.ensure_future(self._parent.terminate_NSR(nsr_id), loop=self._loop)
1457 else:
1458 raise NotImplementedError(
1459 "%s action on cm-state not supported",
1460 query_action)
1461
1462 xact_info.respond_xpath(rwdts.XactRspCode.ACK)
1463
1464 try:
1465 handler = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare)
1466 self.dts_reg_hdl = yield from self._dts.register(self.nsr_xpath,
1467 flags=rwdts.Flag.SUBSCRIBER | rwdts.Flag.DELTA_READY,
1468 handler=handler)
1469 except Exception as e:
1470 self._log.error("Failed to register for NSR changes as %s", str(e))
1471
1472