update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / rwlaunchpad / plugins / rwnsm / rift / tasklets / rwnsmtasklet / openmano_nsm.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 gi
20 import os
21 import sys
22 import time
23 import yaml
24
25 gi.require_version('RwDts', '1.0')
26 gi.require_version('RwVnfrYang', '1.0')
27 from gi.repository import (
28 RwDts as rwdts,
29 RwVnfrYang,
30 )
31 gi.require_version('RwKeyspec', '1.0')
32 from gi.repository.RwKeyspec import quoted_key
33
34 import rift.openmano.rift2openmano as rift2openmano
35 import rift.openmano.openmano_client as openmano_client
36 from . import nsmpluginbase
37 from enum import Enum
38
39 import ipaddress
40 import rift.tasklets
41
42 if sys.version_info < (3, 4, 4):
43 asyncio.ensure_future = asyncio.async
44
45
46 DUMP_OPENMANO_DIR = os.path.join(
47 os.environ["RIFT_VAR_ROOT"],
48 "openmano_descriptors"
49 )
50
51
52 def dump_openmano_descriptor(name, descriptor_str):
53 filename = "{}_{}.yaml".format(
54 time.strftime("%Y%m%d-%H%M%S"),
55 name
56 )
57
58 filepath = os.path.join(
59 DUMP_OPENMANO_DIR,
60 filename
61 )
62
63 try:
64 if not os.path.exists(DUMP_OPENMANO_DIR):
65 os.makedirs(DUMP_OPENMANO_DIR)
66
67 with open(filepath, 'w') as hdl:
68 hdl.write(descriptor_str)
69
70 except OSError as e:
71 print("Failed to dump openmano descriptor: %s" % str(e))
72
73 return filepath
74
75 class VNFExistError(Exception):
76 pass
77
78 class VnfrConsoleOperdataDtsHandler(object):
79 """ registers 'D,/vnfr:vnfr-console/vnfr:vnfr[id]/vdur[id]' and handles CRUD from DTS"""
80 @property
81 def vnfr_vdu_console_xpath(self):
82 """ path for resource-mgr"""
83 return self._project.add_project(
84 "D,/rw-vnfr:vnfr-console/rw-vnfr:vnfr[rw-vnfr:id={}]/rw-vnfr:vdur[vnfr:id={}]".format(
85 quoted_key(self._vnfr_id), quoted_key(self._vdur_id)))
86
87 def __init__(self, project, dts, log, loop, nsr, vnfr_id, vdur_id, vdu_id):
88 self._project = project
89 self._dts = dts
90 self._log = log
91 self._loop = loop
92 self._regh = None
93 self._nsr = nsr
94
95 self._vnfr_id = vnfr_id
96 self._vdur_id = vdur_id
97 self._vdu_id = vdu_id
98
99 @asyncio.coroutine
100 def register(self):
101 """ Register for VNFR VDU Operational Data read from dts """
102
103 @asyncio.coroutine
104 def on_prepare(xact_info, action, ks_path, msg):
105 """ prepare callback from dts """
106 xpath = ks_path.to_xpath(RwVnfrYang.get_schema())
107 self._log.debug(
108 "Got VNFR VDU Opdata xact_info: %s, action: %s): %s:%s",
109 xact_info, action, xpath, msg
110 )
111
112 if action == rwdts.QueryAction.READ:
113 schema = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur.schema()
114 path_entry = schema.keyspec_to_entry(ks_path)
115
116 try:
117 console_url = yield from self._loop.run_in_executor(
118 None,
119 self._nsr._http_api.get_instance_vm_console_url,
120 self._nsr._nsr_uuid,
121 self._vdur_id
122 )
123
124 self._log.debug("Got console response: %s for NSR ID %s vdur ID %s",
125 console_url,
126 self._nsr._nsr_uuid,
127 self._vdur_id
128 )
129 vdur_console = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur()
130 vdur_console.id = self._vdur_id
131 if console_url:
132 vdur_console.console_url = console_url
133 else:
134 vdur_console.console_url = 'none'
135 self._log.debug("Recevied console URL for vdu {} is {}".format(self._vdu_id,vdur_console))
136 except openmano_client.InstanceStatusError as e:
137 self._log.error("Could not get NS instance console URL: %s",
138 str(e))
139 vdur_console = RwVnfrYang.YangData_RwProject_Project_VnfrConsole_Vnfr_Vdur()
140 vdur_console.id = self._vdur_id
141 vdur_console.console_url = 'none'
142
143 xact_info.respond_xpath(rsp_code=rwdts.XactRspCode.ACK,
144 xpath=self.vnfr_vdu_console_xpath,
145 msg=vdur_console)
146 else:
147 #raise VnfRecordError("Not supported operation %s" % action)
148 self._log.error("Not supported operation %s" % action)
149 xact_info.respond_xpath(rsp_code=rwdts.XactRspCode.ACK)
150 return
151
152 self._log.debug("Registering for VNFR VDU using xpath: %s",
153 self.vnfr_vdu_console_xpath)
154 hdl = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare,)
155 with self._dts.group_create() as group:
156 self._regh = group.register(xpath=self.vnfr_vdu_console_xpath,
157 handler=hdl,
158 flags=rwdts.Flag.PUBLISHER,
159 )
160
161
162
163 class OpenmanoVnfr(object):
164 def __init__(self, log, loop, cli_api, http_api, vnfr, nsd, ssh_key=None):
165 self._log = log
166 self._loop = loop
167 self._cli_api = cli_api
168 self._http_api = http_api
169 self._vnfr = vnfr
170 self._vnfd_id = vnfr.vnfd.id
171
172 self._vnf_id = None
173
174 self._created = False
175
176 self.nsd = nsd
177 self._ssh_key = ssh_key
178
179 @property
180 def vnfd(self):
181 return rift2openmano.RiftVNFD(self._vnfr.vnfd)
182
183 @property
184 def vnfr(self):
185 return self._vnfr
186
187 @property
188 def rift_vnfd_id(self):
189 return self._vnfd_id
190
191 @property
192 def openmano_vnfd_id(self):
193 return self._vnf_id
194
195 @property
196 def openmano_vnfd(self):
197 self._log.debug("Converting vnfd %s from rift to openmano", self.vnfd.id)
198 openmano_vnfd = rift2openmano.rift2openmano_vnfd(self.vnfd, self.nsd, self._http_api)
199 return openmano_vnfd
200
201 @property
202 def openmano_vnfd_yaml(self):
203 return yaml.safe_dump(self.openmano_vnfd, default_flow_style=False)
204
205 @asyncio.coroutine
206 def create(self):
207 try:
208 self._log.debug("Created openmano vnfd")
209 # The self.openmano_vnfd_yaml internally creates the vnf if not found.
210 # Assigning the yaml to a variable so that the api is not fired unnecessarily.
211 openmano_vnfd = self.openmano_vnfd
212 name = openmano_vnfd["name"]
213
214 self._vnf_id = openmano_vnfd['uuid']
215
216 self._created = True
217 except Exception as e:
218 self._log.error("Failed to create vnf on Openmano RO : %s", e)
219 raise e
220
221 def delete(self):
222 if not self._created:
223 return
224
225 self._log.debug("Deleting openmano vnfd")
226 if self._vnf_id is None:
227 self._log.warning("Openmano vnf id not set. Cannot delete.")
228 return
229
230 self._cli_api.vnf_delete(self._vnf_id)
231
232
233 class OpenmanoNSRecordState(Enum):
234 """ Network Service Record State """
235 # Make sure the values match with NetworkServiceRecordState
236 INIT = 101
237 INSTANTIATION_PENDING = 102
238 RUNNING = 106
239 SCALING_OUT = 107
240 SCALING_IN = 108
241 TERMINATE = 109
242 TERMINATE_RCVD = 110
243 TERMINATED = 114
244 FAILED = 115
245 VL_INSTANTIATE = 116
246 VL_TERMINATE = 117
247
248
249 class OpenmanoNsr(object):
250 TIMEOUT_SECS = 300
251 INSTANCE_TERMINATE_TIMEOUT = 60
252
253 def __init__(self, project, dts, log, loop, publisher, cli_api, http_api, nsd_msg,
254 nsr_config_msg, key_pairs, ssh_key, rift_vnfd_id=None ):
255 self._project = project
256 self._log = log
257 self._dts = dts
258 self._loop = loop
259 self._publisher = publisher
260 self._cli_api = cli_api
261 self._http_api = http_api
262
263 self._nsd_msg = nsd_msg
264 self._nsr_config_msg = nsr_config_msg
265 self._vlrs = []
266 self._vnfrs = []
267 self._nsrs = {}
268 self._vdur_console_handler = {}
269 self._key_pairs = key_pairs
270 self._ssh_key = ssh_key
271
272 self._nsd_uuid = None
273 self._nsr_uuid = None
274 self._nsd_msg = nsd_msg
275
276 self._nsr_msg = None
277
278 self._created = False
279
280 self._monitor_task = None
281 self._rift_vnfd_id = rift_vnfd_id
282 self._state = OpenmanoNSRecordState.INIT
283
284 self._active_vms = 0
285 self._active_nets = 0
286
287 @property
288 def nsd(self):
289 return rift2openmano.RiftNSD(self._nsd_msg)
290
291 @property
292 def rift_vnfd_id(self):
293 return self._rift_vnfd_id
294
295 @property
296 def nsd_msg(self):
297 return self._nsd_msg
298
299 @property
300 def nsr_config_msg(self):
301 return self._nsr_config_msg
302
303
304 @property
305 def vnfds(self):
306 return {v.rift_vnfd_id: v.vnfd for v in self._vnfrs}
307
308 @property
309 def vnfr_ids(self):
310 return {v.rift_vnfd_id: v.openmano_vnfd_id for v in self._vnfrs}
311
312 @property
313 def vnfrs(self):
314 return self._vnfrs
315
316 @property
317 def key_pairs(self):
318 return self._key_pairs
319
320 @property
321 def nsr_msg(self):
322 return self._nsr_msg
323
324 @property
325 def vlrs(self):
326 return self._vlrs
327
328 @property
329 def http_api(self):
330 return self._http_api
331
332 @property
333 def openmano_nsd_yaml(self):
334 self._log.debug("Converting nsd %s from rift to openmano", self.nsd.id)
335 openmano_nsd = rift2openmano.rift2openmano_nsd(self.nsd, self.vnfds,self.vnfr_ids, self.http_api)
336 return yaml.safe_dump(openmano_nsd, default_flow_style=False)
337
338 @property
339 def openmano_scaling_yaml(self):
340 self._log.debug("Creating Openmano Scaling Descriptor %s")
341 try:
342 openmano_vnfd_nsd = rift2openmano.rift2openmano_vnfd_nsd(self.nsd, self.vnfds, self.vnfr_ids, self.http_api, self._rift_vnfd_id)
343 return yaml.safe_dump(openmano_vnfd_nsd, default_flow_style=False)
344 except Exception as e:
345 self._log.exception("Scaling Descriptor Exception: %s", str(e))
346
347 def get_ssh_key_pairs(self):
348 cloud_config = {}
349 key_pairs = list()
350 for authorized_key in self._nsr_config_msg.ssh_authorized_key:
351 self._log.debug("Key pair ref present is %s",authorized_key.key_pair_ref)
352 if authorized_key.key_pair_ref in self._key_pairs:
353 key_pairs.append(self._key_pairs[authorized_key.key_pair_ref].key)
354
355 for authorized_key in self._nsd_msg.key_pair:
356 self._log.debug("Key pair NSD is %s",authorized_key)
357 key_pairs.append(authorized_key.key)
358
359 if self._ssh_key['public_key']:
360 self._log.debug("Pub key NSD is %s", self._ssh_key['public_key'])
361 key_pairs.append(self._ssh_key['public_key'])
362
363 if key_pairs:
364 cloud_config["key-pairs"] = key_pairs
365
366 users = list()
367 for user_entry in self._nsr_config_msg.user:
368 self._log.debug("User present is %s",user_entry)
369 user = {}
370 user["name"] = user_entry.name
371 user["key-pairs"] = list()
372 for ssh_key in user_entry.ssh_authorized_key:
373 if ssh_key.key_pair_ref in self._key_pairs:
374 user["key-pairs"].append(self._key_pairs[ssh_key.key_pair_ref].key)
375 users.append(user)
376
377 for user_entry in self._nsd_msg.user:
378 self._log.debug("User present in NSD is %s",user_entry)
379 user = {}
380 user["name"] = user_entry.name
381 user["key-pairs"] = list()
382 for ssh_key in user_entry.key_pair:
383 user["key-pairs"].append(ssh_key.key)
384 users.append(user)
385
386 if users:
387 cloud_config["users"] = users
388
389 self._log.debug("Cloud config formed is %s",cloud_config)
390 return cloud_config
391
392
393 @property
394 def openmano_instance_create_yaml(self):
395 self._log.debug("Creating instance-scenario-create input file for nsd %s with name %s", self.nsd.id, self._nsr_config_msg.name)
396 openmano_instance_create = {}
397 openmano_instance_create["name"] = self._nsr_config_msg.name
398 openmano_instance_create["description"] = self._nsr_config_msg.description
399 openmano_instance_create["scenario"] = self._nsd_uuid
400
401 cloud_config = self.get_ssh_key_pairs()
402 if cloud_config:
403 openmano_instance_create["cloud-config"] = cloud_config
404 if self._nsr_config_msg.has_field("datacenter"):
405 openmano_instance_create["datacenter"] = self._nsr_config_msg.datacenter
406 openmano_instance_create["vnfs"] = {}
407 for vnfr in self._vnfrs:
408 if "datacenter" in vnfr.vnfr.vnfr_msg:
409 vnfr_name = vnfr.vnfr.vnfd.name + "." + str(vnfr.vnfr.vnfr_msg.member_vnf_index_ref)
410 openmano_instance_create["vnfs"][vnfr_name] = {"datacenter": vnfr.vnfr.vnfr_msg.datacenter}
411 openmano_instance_create["networks"] = {}
412 for vld_msg in self._nsd_msg.vld:
413 openmano_instance_create["networks"][vld_msg.name] = {}
414 openmano_instance_create["networks"][vld_msg.name]["sites"] = list()
415 for vlr in self._vlrs:
416 if vlr.vld_msg.name == vld_msg.name:
417 self._log.debug("Received VLR name %s, VLR DC: %s for VLD: %s",vlr.vld_msg.name,
418 vlr.datacenter_name,vld_msg.name)
419 #network["vim-network-name"] = vld_msg.name
420 network = {}
421 ip_profile = {}
422 if vld_msg.vim_network_name:
423 network["netmap-use"] = vld_msg.vim_network_name
424 elif vlr._ip_profile and vlr._ip_profile.has_field("ip_profile_params"):
425 ip_profile_params = vlr._ip_profile.ip_profile_params
426 if ip_profile_params.ip_version == "ipv6":
427 ip_profile['ip-version'] = "IPv6"
428 else:
429 ip_profile['ip-version'] = "IPv4"
430 if ip_profile_params.has_field('subnet_address'):
431 ip_profile['subnet-address'] = ip_profile_params.subnet_address
432 if ip_profile_params.has_field('gateway_address'):
433 ip_profile['gateway-address'] = ip_profile_params.gateway_address
434 if ip_profile_params.has_field('dns_server') and len(ip_profile_params.dns_server) > 0:
435 ip_profile['dns-address'] = ip_profile_params.dns_server[0].address
436 if ip_profile_params.has_field('dhcp_params'):
437 ip_profile['dhcp'] = {}
438 ip_profile['dhcp']['enabled'] = ip_profile_params.dhcp_params.enabled
439 ip_profile['dhcp']['start-address'] = ip_profile_params.dhcp_params.start_address
440 ip_profile['dhcp']['count'] = ip_profile_params.dhcp_params.count
441 if ip_profile['dhcp']['enabled'] is True and ip_profile['dhcp']['start-address'] is None:
442 addr_pool = list(ipaddress.ip_network(ip_profile['subnet-address']).hosts())
443 gateway_ip_addr = ip_profile.get('gateway-address', None)
444 if gateway_ip_addr is None:
445 gateway_ip_addr = str(next(iter(addr_pool)))
446 ip_profile['gateway-address'] = gateway_ip_addr
447
448 self._log.debug("Gateway Address {}".format(gateway_ip_addr))
449
450 if ipaddress.ip_address(gateway_ip_addr) in addr_pool:
451 addr_pool.remove(ipaddress.ip_address(gateway_ip_addr))
452 if len(addr_pool) > 0:
453 ip_profile['dhcp']['start-address'] = str(next(iter(addr_pool)))
454 #DHCP count more than 200 is not instantiating any instances using OPENMANO RO
455 #So restricting it to a feasible count of 100.
456 dhcp_count = ip_profile['dhcp']['count']
457 if dhcp_count is None or dhcp_count == 0 or dhcp_count > len(addr_pool):
458 ip_profile['dhcp']['count'] = min(len(addr_pool), 100)
459 self._log.debug("DHCP start Address {} DHCP count {}".
460 format(ip_profile['dhcp']['start-address'], ip_profile['dhcp']['count']))
461 else:
462 network["netmap-create"] = vlr.name
463 if vlr.datacenter_name:
464 network["datacenter"] = vlr.datacenter_name
465 elif vld_msg.has_field("datacenter"):
466 network["datacenter"] = vld_msg.datacenter
467 elif "datacenter" in openmano_instance_create:
468 network["datacenter"] = openmano_instance_create["datacenter"]
469 if network:
470 openmano_instance_create["networks"][vld_msg.name]["sites"].append(network)
471 if ip_profile:
472 openmano_instance_create["networks"][vld_msg.name]['ip-profile'] = ip_profile
473
474 return yaml.safe_dump(openmano_instance_create, default_flow_style=False,width=1000)
475
476 @property
477 def scaling_instance_create_yaml(self, scaleout=False):
478 self._log.debug("Creating instance-scenario-create input file for nsd %s with name %s", self.nsd.id, self._nsr_config_msg.name+"scal1")
479 scaling_instance_create = {}
480 for group_list in self._nsd_msg.scaling_group_descriptor:
481 scaling_instance_create["name"] = self._nsr_config_msg.name + "__"+group_list.name
482 if scaleout:
483 scaling_instance_create["scenario"] = self._nsd_uuid + "__" +group_list.name
484 else:
485 scaling_instance_create["scenario"] = self._nsd_uuid
486 scaling_instance_create["description"] = self._nsr_config_msg.description
487
488
489 if self._nsr_config_msg.has_field("datacenter"):
490 scaling_instance_create["datacenter"] = self._nsr_config_msg.datacenter
491 scaling_instance_create["vnfs"] = {}
492 for vnfr in self._vnfrs:
493 if "datacenter" in vnfr.vnfr.vnfr_msg:
494 vnfr_name = vnfr.vnfr.vnfd.name + "__" + str(vnfr.vnfr.vnfr_msg.member_vnf_index_ref)
495 scaling_instance_create["vnfs"][vnfr_name] = {"datacenter": vnfr.vnfr.vnfr_msg.datacenter}
496 scaling_instance_create["networks"] = {}
497 for vld_msg in self._nsd_msg.vld:
498 scaling_instance_create["networks"][vld_msg.name] = {}
499 scaling_instance_create["networks"][vld_msg.name]["sites"] = list()
500 for vlr in self._vlrs:
501 if vlr.vld_msg.name == vld_msg.name:
502 self._log.debug("Received VLR name %s, VLR DC: %s for VLD: %s",vlr.vld_msg.name,
503 vlr.datacenter_name,vld_msg.name)
504 #network["vim-network-name"] = vld_msg.name
505 network = {}
506 ip_profile = {}
507 if vld_msg.vim_network_name:
508 network["netmap-use"] = vld_msg.vim_network_name
509 #else:
510 # network["netmap-create"] = vlr.name
511 if vlr.datacenter_name:
512 network["datacenter"] = vlr.datacenter_name
513 elif vld_msg.has_field("datacenter"):
514 network["datacenter"] = vld_msg.datacenter
515 elif "datacenter" in scaling_instance_create:
516 network["datacenter"] = scaling_instance_create["datacenter"]
517 if network:
518 scaling_instance_create["networks"][vld_msg.name]["sites"].append(network)
519
520 return yaml.safe_dump(scaling_instance_create, default_flow_style=False, width=1000)
521
522 @asyncio.coroutine
523 def add_vlr(self, vlr):
524 self._vlrs.append(vlr)
525 yield from self._publisher.publish_vlr(None, vlr.vlr_msg)
526 yield from asyncio.sleep(1, loop=self._loop)
527
528 @asyncio.coroutine
529 def remove_vlr(self, vlr):
530 if vlr in self._vlrs:
531 self._vlrs.remove(vlr)
532 yield from self._publisher.unpublish_vlr(None, vlr.vlr_msg)
533 yield from asyncio.sleep(1, loop=self._loop)
534
535 @asyncio.coroutine
536 def remove_vnf(self,vnf):
537 if vnf in self._vnfrs:
538 self._vnfrs.remove(vnf)
539 yield from self._publisher.unpublish_vnfr(
540 None,
541 vnfr_msg
542 )
543 yield from asyncio.sleep(1, loop=self._loop)
544
545 @asyncio.coroutine
546 def delete_vlr(self, vlr):
547 if vlr in self._vlrs:
548 self._vlrs.remove(vlr)
549 if not vlr.vld_msg.vim_network_name:
550 yield from self._loop.run_in_executor(
551 None,
552 self._cli_api.ns_vim_network_delete,
553 vlr.name,
554 vlr.datacenter_name)
555 yield from self._publisher.unpublish_vlr(None, vlr.vlr_msg)
556 yield from asyncio.sleep(1, loop=self._loop)
557
558 @asyncio.coroutine
559 def add_vnfr(self, vnfr):
560 vnfr = OpenmanoVnfr(self._log, self._loop, self._cli_api, self.http_api,
561 vnfr, nsd=self.nsd, ssh_key=self._ssh_key)
562 yield from vnfr.create()
563 self._vnfrs.append(vnfr)
564
565 @asyncio.coroutine
566 def add_nsr(self, nsr, vnfr):
567 self._nsrs[vnfr.id] = nsr
568
569 def delete(self):
570 if not self._created:
571 self._log.debug("NSD wasn't created. Skipping delete.")
572 return
573
574 self._log.debug("Deleting openmano nsr")
575 self._cli_api.ns_delete(self._nsd_uuid)
576
577 self._log.debug("Deleting openmano vnfrs")
578 deleted_vnf_id_list = []
579 for vnfr in self._vnfrs:
580 if vnfr.vnfr.vnfd.id not in deleted_vnf_id_list:
581 vnfr.delete()
582 deleted_vnf_id_list.append(vnfr.vnfr.vnfd.id)
583
584
585 @asyncio.coroutine
586 def create(self):
587 try:
588 self._log.debug("Created openmano scenario")
589 # The self.openmano_nsd_yaml internally creates the scenario if not found.
590 # Assigning the yaml to a variable so that the api is not fired unnecessarily.
591 nsd_yaml = self.openmano_nsd_yaml
592
593 self._nsd_uuid = yaml.load(nsd_yaml)['uuid']
594 fpath = dump_openmano_descriptor(
595 "{}_nsd".format(self._nsd_msg.name),
596 nsd_yaml,
597 )
598
599 self._log.debug("Dumped Openmano NS descriptor to: %s", fpath)
600
601 self._created = True
602 except Exception as e:
603 self._log.error("Failed to create scenario on Openmano RO : %s", e)
604 raise e
605
606 @asyncio.coroutine
607 def scaling_scenario_create(self):
608 self._log.debug("Creating scaling openmano scenario")
609
610 # The self.openmano_nsd_yaml internally creates the scenario if not found.
611 # Assigning the yaml to a variable so that the api is not fired unnecessarily.
612 nsd_yaml = self.openmano_scaling_yaml
613
614 self._nsd_uuid = yaml.load(nsd_yaml)['uuid']
615
616 fpath = dump_openmano_descriptor(
617 "{}_sgd".format(self._nsd_msg.name),
618 self.scaling_instance_create_yaml,
619 )
620
621
622 @asyncio.coroutine
623 def get_nsr_opdata(self):
624 """ NSR opdata associated with this VNFR """
625 xpath = self._project.add_project(
626 "D,/nsr:ns-instance-opdata/nsr:nsr" \
627 "[nsr:ns-instance-config-ref={}]". \
628 format(quoted_key(self.nsr_config_msg.id)))
629
630 results = yield from self._dts.query_read(xpath, rwdts.XactFlag.MERGE)
631
632 for result in results:
633 entry = yield from result
634 nsr_op = entry.result
635 return nsr_op
636
637 return None
638
639
640 @asyncio.coroutine
641 def instance_monitor_task(self):
642 self._log.debug("Starting Instance monitoring task")
643
644 start_time = time.time()
645 active_vnfs = []
646 nsr = yield from self.get_nsr_opdata()
647 while True:
648 active_vms = 0
649 active_nets = 0
650
651 yield from asyncio.sleep(1, loop=self._loop)
652
653 try:
654 instance_resp_json = yield from self._loop.run_in_executor(
655 None,
656 self._http_api.get_instance,
657 self._nsr_uuid,
658 )
659
660 self._log.debug("Got instance response: %s for NSR ID %s",
661 instance_resp_json,
662 self._nsr_uuid)
663
664 for vnf in instance_resp_json['vnfs']:
665 for vm in vnf['vms']:
666 if vm['status'] == 'ACTIVE':
667 active_vms += 1
668 for net in instance_resp_json['nets']:
669 if net['status'] == 'ACTIVE':
670 active_nets += 1
671
672 nsr.orchestration_progress.vms.active = active_vms
673 nsr.orchestration_progress.networks.active = active_nets
674
675 # This is for accesibility of the status from nsm when the control goes back.
676 self._active_vms = active_vms
677 self._active_nets = active_nets
678
679 yield from self._publisher.publish_nsr_opdata(None, nsr)
680
681 except openmano_client.InstanceStatusError as e:
682 self._log.error("Could not get NS instance status: %s", str(e))
683 continue
684
685
686 def all_vms_active(vnf):
687 for vm in vnf["vms"]:
688 vm_status = vm["status"]
689 vm_uuid = vm["uuid"]
690 if vm_status != "ACTIVE":
691 self._log.debug("VM is not yet active: %s (status: %s)", vm_uuid, vm_status)
692 return False
693
694 return True
695
696 def any_vm_active_nomgmtip(vnf):
697 for vm in vnf["vms"]:
698 vm_status = vm["status"]
699 vm_uuid = vm["uuid"]
700 if vm_status != "ACTIVE":
701 self._log.debug("VM is not yet active: %s (status: %s)", vm_uuid, vm_status)
702 return False
703
704 return True
705
706 def any_vms_error(vnf):
707 for vm in vnf["vms"]:
708 vm_status = vm["status"]
709 vm_vim_info = vm["vim_info"]
710 vm_uuid = vm["uuid"]
711 if vm_status == "ERROR":
712 self._log.error("VM Error: %s (vim_info: %s)", vm_uuid, vm_vim_info)
713 return True
714
715 return False
716
717 def get_vnf_ip_address(vnf):
718 if "ip_address" in vnf:
719 return vnf["ip_address"].strip()
720
721 else:
722 cp_info_list = get_ext_cp_info(vnf)
723
724 for cp_name, ip, mac in cp_info_list:
725 for vld in self.nsd.vlds:
726 if not vld.mgmt_network:
727 continue
728
729 for vld_cp in vld.vnfd_connection_point_ref:
730 if vld_cp.vnfd_connection_point_ref == cp_name:
731 return ip
732 return None
733
734 def get_vnf_mac_address(vnf):
735 if "mac_address" in vnf:
736 return vnf["mac_address"].strip()
737 return None
738
739 def get_ext_cp_info(vnf):
740 cp_info_list = []
741 for vm in vnf["vms"]:
742 if "interfaces" not in vm:
743 continue
744
745 for intf in vm["interfaces"]:
746 if "external_name" not in intf:
747 continue
748
749 if not intf["external_name"]:
750 continue
751
752 ip_address = intf["ip_address"]
753 if ip_address is None:
754 ip_address = "0.0.0.0"
755
756 mac_address = intf["mac_address"]
757 if mac_address is None:
758 mac_address="00:00:00:00:00:00"
759
760 cp_info_list.append((intf["external_name"], ip_address, mac_address))
761
762 return cp_info_list
763
764 def get_vnf_status(vnfr):
765 # When we create an openmano descriptor we use <name>.<idx>
766 # to come up with openmano constituent VNF name. Use this
767 # knowledge to map the vnfr back.
768 openmano_vnfr_suffix = ".{}".format(
769 vnfr.vnfr.vnfr_msg.member_vnf_index_ref
770 )
771
772 for vnf in instance_resp_json["vnfs"]:
773 if vnf["vnf_name"].endswith(openmano_vnfr_suffix):
774 return vnf
775
776 self._log.warning("Could not find vnf status with name that ends with: %s",
777 openmano_vnfr_suffix)
778 return None
779
780 for vnfr in self._vnfrs:
781 if vnfr in active_vnfs:
782 # Skipping, so we don't re-publish the same VNF message.
783 continue
784
785 vnfr_msg = vnfr.vnfr.vnfr_msg.deep_copy()
786 vnfr_msg.operational_status = "init"
787
788 try:
789 vnf_status = get_vnf_status(vnfr)
790 self._log.debug("Found VNF status: %s", vnf_status)
791 if vnf_status is None:
792 self._log.error("Could not find VNF status from openmano")
793 self._state = OpenmanoNSRecordState.FAILED
794 vnfr_msg.operational_status = "failed"
795 yield from self._publisher.publish_vnfr(None, vnfr_msg)
796 return
797
798 # If there was a VNF that has a errored VM, then just fail the VNF and stop monitoring.
799 if any_vms_error(vnf_status):
800 self._log.error("VM was found to be in error state. Marking as failed.")
801 self._state = OpenmanoNSRecordState.FAILED
802 vnfr_msg.operational_status = "failed"
803 yield from self._publisher.publish_vnfr(None, vnfr_msg)
804 return
805
806 if (time.time() - start_time) > OpenmanoNsr.TIMEOUT_SECS:
807 self._log.error("NSR timed out before reaching running state")
808 self._state = OpenmanoNSRecordState.FAILED
809 vnfr_msg.operational_status = "failed"
810 yield from self._publisher.publish_vnfr(None, vnfr_msg)
811 return
812
813 if all_vms_active(vnf_status):
814 vnf_ip_address = get_vnf_ip_address(vnf_status)
815 vnf_mac_address = get_vnf_mac_address(vnf_status)
816
817 if vnf_ip_address is None:
818 self._log.error("No IP address obtained "
819 "for VNF: {}, will retry.".format(
820 vnf_status['vnf_name']))
821 continue
822
823 self._log.debug("All VMs in VNF are active. Marking as running.")
824 vnfr_msg.operational_status = "running"
825
826 self._log.debug("Got VNF ip address: %s, mac-address: %s",
827 vnf_ip_address, vnf_mac_address)
828 vnfr_msg.mgmt_interface.ip_address = vnf_ip_address
829 vnfr_msg.mgmt_interface.ssh_key.public_key = \
830 vnfr._ssh_key['public_key']
831 vnfr_msg.mgmt_interface.ssh_key.private_key_file = \
832 vnfr._ssh_key['private_key']
833
834 for vm in vnf_status["vms"]:
835 if vm["uuid"] not in self._vdur_console_handler:
836 vdur_console_handler = VnfrConsoleOperdataDtsHandler(self._project, self._dts, self._log, self._loop,
837 self, vnfr_msg.id,vm["uuid"],vm["name"])
838 yield from vdur_console_handler.register()
839 self._vdur_console_handler[vm["uuid"]] = vdur_console_handler
840
841 vdur_msg = vnfr_msg.vdur.add()
842 vdur_msg.vim_id = vm["vim_vm_id"]
843 vdur_msg.id = vm["uuid"]
844
845 # Add connection point information for the config manager
846 cp_info_list = get_ext_cp_info(vnf_status)
847 for (cp_name, cp_ip, cp_mac_addr) in cp_info_list:
848 cp = vnfr_msg.connection_point.add()
849 cp.name = cp_name
850 cp.short_name = cp_name
851 cp.ip_address = cp_ip
852 cp.mac_address = cp_mac_addr
853
854 yield from self._publisher.publish_vnfr(None, vnfr_msg)
855 active_vnfs.append(vnfr)
856
857 except Exception as e:
858 vnfr_msg.operational_status = "failed"
859 self._state = OpenmanoNSRecordState.FAILED
860 yield from self._publisher.publish_vnfr(None, vnfr_msg)
861 self._log.exception("Caught exception publishing vnfr info: %s", str(e))
862 return
863
864 if len(active_vnfs) == len(self._vnfrs):
865 self._state = OpenmanoNSRecordState.RUNNING
866 self._log.debug("All VNF's are active. Exiting NSR monitoring task")
867 return
868
869 @asyncio.coroutine
870 def deploy(self,nsr_msg):
871 if self._nsd_uuid is None:
872 raise ValueError("Cannot deploy an uncreated nsd")
873
874 self._log.debug("Deploying openmano instance scenario")
875
876 name_uuid_map = yield from self._loop.run_in_executor(
877 None,
878 self._cli_api.ns_instance_list,
879 )
880
881 if self._nsr_config_msg.name in name_uuid_map:
882 self._log.debug("Found existing instance with nsr name: %s", self._nsr_config_msg.name)
883 self._nsr_uuid = name_uuid_map[self._nsr_config_msg.name]
884 else:
885 self._nsr_msg = nsr_msg
886 fpath = dump_openmano_descriptor(
887 "{}_instance_sce_create".format(self._nsr_config_msg.name),
888 self.openmano_instance_create_yaml,
889 )
890 self._log.debug("Dumped Openmano instance Scenario Cretae to: %s", fpath)
891
892 self._nsr_uuid = yield from self._loop.run_in_executor(
893 None,
894 self._cli_api.ns_instance_scenario_create,
895 self.openmano_instance_create_yaml)
896
897 self._state = OpenmanoNSRecordState.INSTANTIATION_PENDING
898
899 self._monitor_task = asyncio.ensure_future(
900 self.instance_monitor_task(), loop=self._loop
901 )
902
903 @asyncio.coroutine
904 def deploy_scaling(self, nsr_msg, rift_vnfd_id):
905 self._log.debug("Deploying Scaling instance scenario")
906 self._nsr_msg = nsr_msg
907 self._rift_vnfd_id = rift_vnfd_id
908 fpath = dump_openmano_descriptor(
909 "{}_scale_instance".format(self._nsr_config_msg.name),
910 self.scaling_instance_create_yaml
911 )
912 self._nsr_uuid = yield from self._loop.run_in_executor(
913 None,
914 self._cli_api.ns_instance_scenario_create,
915 self.scaling_instance_create_yaml)
916
917 self._state = OpenmanoNSRecordState.INSTANTIATION_PENDING
918
919 self._monitor_task = asyncio.ensure_future(
920 self.instance_monitor_task(), loop=self._loop
921 )
922
923 self._state = OpenmanoNSRecordState.INIT
924
925
926 def terminate(self):
927 if self._nsr_uuid is None:
928 start_time = time.time()
929 while ((time.time() - start_time) < OpenmanoNsr.INSTANCE_TERMINATE_TIMEOUT) and (self._nsr_uuid is None):
930 time.sleep(5)
931 self._log.warning("Waiting for nsr to get instatiated")
932 if self._nsr_uuid is None:
933 self._log.warning("Cannot terminate an un-instantiated nsr")
934 return
935
936 if self._monitor_task is not None:
937 self._monitor_task.cancel()
938 self._monitor_task = None
939
940 self._log.debug("Terminating openmano nsr")
941 self._cli_api.ns_terminate(self._nsr_uuid)
942
943 @asyncio.coroutine
944 def create_vlr(self,vlr):
945 self._log.error("Creating openmano vim network VLR name %s, VLR DC: %s",vlr.vld_msg.name,
946 vlr.datacenter_name)
947 net_create = {}
948 net = {}
949 net['name'] = vlr.name
950 net['shared'] = True
951 net['type'] = 'bridge'
952 self._log.error("Received ip profile is %s",vlr._ip_profile)
953 if vlr._ip_profile and vlr._ip_profile.has_field("ip_profile_params"):
954 ip_profile_params = vlr._ip_profile.ip_profile_params
955 ip_profile = {}
956 if ip_profile_params.ip_version == "ipv6":
957 ip_profile['ip_version'] = "IPv6"
958 else:
959 ip_profile['ip_version'] = "IPv4"
960 if ip_profile_params.has_field('subnet_address'):
961 ip_profile['subnet_address'] = ip_profile_params.subnet_address
962 if ip_profile_params.has_field('gateway_address'):
963 ip_profile['gateway_address'] = ip_profile_params.gateway_address
964 if ip_profile_params.has_field('dns_server') and len(ip_profile_params.dns_server) > 0:
965 ip_profile['dns_address'] = ip_profile_params.dns_server[0].address
966 if ip_profile_params.has_field('dhcp_params'):
967 ip_profile['dhcp_enabled'] = ip_profile_params.dhcp_params.enabled
968 ip_profile['dhcp_start_address'] = ip_profile_params.dhcp_params.start_address
969 ip_profile['dhcp_count'] = ip_profile_params.dhcp_params.count
970 net['ip_profile'] = ip_profile
971 net_create["network"]= net
972
973 net_create_msg = yaml.safe_dump(net_create,default_flow_style=False)
974 fpath = dump_openmano_descriptor(
975 "{}_vim_net_create_{}".format(self._nsr_config_msg.name,vlr.name),
976 net_create_msg)
977 self._log.error("Dumped Openmano VIM Net create to: %s", fpath)
978
979 vim_network_uuid = yield from self._loop.run_in_executor(
980 None,
981 self._cli_api.ns_vim_network_create,
982 net_create_msg,
983 vlr.datacenter_name)
984 self._vlrs.append(vlr)
985
986
987
988 class OpenmanoNsPlugin(nsmpluginbase.NsmPluginBase):
989 """
990 RW Implentation of the NsmPluginBase
991 """
992 def __init__(self, dts, log, loop, publisher, ro_account, project):
993 self._dts = dts
994 self._log = log
995 self._loop = loop
996 self._publisher = publisher
997 self._project = project
998
999 self._cli_api = None
1000 self._http_api = None
1001 self._openmano_nsrs = {}
1002 self._openmano_nsr_by_vnfr_id = {}
1003 #self._nsr_uuid = None
1004
1005 self._set_ro_account(ro_account)
1006
1007 def _set_ro_account(self, ro_account):
1008 self._log.debug("Setting openmano plugin cloud account: %s", ro_account)
1009 self._cli_api = openmano_client.OpenmanoCliAPI(
1010 self.log,
1011 ro_account.openmano.host,
1012 ro_account.openmano.port,
1013 ro_account.openmano.tenant_id,
1014 )
1015
1016 self._http_api = openmano_client.OpenmanoHttpAPI(
1017 self.log,
1018 ro_account.openmano.host,
1019 ro_account.openmano.port,
1020 ro_account.openmano.tenant_id,
1021 )
1022
1023 def set_state(self, nsr_id, state):
1024 # Currently we update only during terminate to
1025 # decide how to handle VL terminate
1026 if state.value == OpenmanoNSRecordState.TERMINATE.value:
1027 self._openmano_nsrs[nsr_id]._state = \
1028 [member.value for name, member in \
1029 OpenmanoNSRecordState.__members__.items() \
1030 if member.value == state.value]
1031
1032 def create_nsr(self, nsr_config_msg, nsd_msg, key_pairs=None, ssh_key=None):
1033 """
1034 Create Network service record
1035 """
1036 openmano_nsr = OpenmanoNsr(
1037 self._project,
1038 self._dts,
1039 self._log,
1040 self._loop,
1041 self._publisher,
1042 self._cli_api,
1043 self._http_api,
1044 nsd_msg,
1045 nsr_config_msg,
1046 key_pairs,
1047 ssh_key,
1048 )
1049 self.log.debug("NSR created in openmano nsm %s", openmano_nsr)
1050 self._openmano_nsrs[nsr_config_msg.id] = openmano_nsr
1051
1052 @asyncio.coroutine
1053 def deploy(self, nsr_msg):
1054 self._log.debug("Received NSR Deploy msg : %s", nsr_msg)
1055 openmano_nsr = self._openmano_nsrs[nsr_msg.ns_instance_config_ref]
1056 yield from openmano_nsr.create()
1057 yield from openmano_nsr.deploy(nsr_msg)
1058
1059 @asyncio.coroutine
1060 def instantiate_ns(self, nsr, xact):
1061 """
1062 Instantiate NSR with the passed nsr id
1063 """
1064 yield from nsr.instantiate(xact)
1065
1066 @asyncio.coroutine
1067 def instantiate_vnf(self, nsr, vnfr, scaleout=False):
1068 """
1069 Instantiate NSR with the passed nsr id
1070 """
1071 openmano_nsr = self._openmano_nsrs[nsr.id]
1072 if scaleout:
1073 openmano_vnf_nsr = OpenmanoNsr(
1074 self._project,
1075 self._dts,
1076 self._log,
1077 self._loop,
1078 self._publisher,
1079 self._cli_api,
1080 self._http_api,
1081 openmano_nsr.nsd_msg,
1082 openmano_nsr.nsr_config_msg,
1083 openmano_nsr.key_pairs,
1084 None,
1085 rift_vnfd_id=vnfr.vnfd.id,
1086 )
1087 self._openmano_nsr_by_vnfr_id[nsr.id] = openmano_nsr
1088 if vnfr.id in self._openmano_nsr_by_vnfr_id:
1089 raise VNFExistError("VNF %s already exist", vnfr.id)
1090 self._openmano_nsr_by_vnfr_id[vnfr.id] = openmano_vnf_nsr
1091 self._log.debug("VNFRID %s %s %s", type(self._openmano_nsr_by_vnfr_id), type(openmano_vnf_nsr), type(self._openmano_nsr_by_vnfr_id[vnfr.id]))
1092
1093 for vlr in openmano_nsr.vlrs:
1094 yield from openmano_vnf_nsr.add_vlr(vlr)
1095 try:
1096 yield from openmano_nsr.add_nsr(openmano_vnf_nsr, vnfr)
1097 except Exception as e:
1098 self.log.exception(str(e))
1099 try:
1100 yield from openmano_vnf_nsr.add_vnfr(vnfr)
1101 except Exception as e:
1102 self.log.exception(str(e))
1103 try:
1104 yield from openmano_vnf_nsr.scaling_scenario_create()
1105 except Exception as e:
1106 self.log.exception(str(e))
1107 try:
1108 yield from openmano_vnf_nsr.deploy_scaling(openmano_vnf_nsr.nsr_msg, vnfr.id)
1109 except Exception as e:
1110 self.log.exception(str(e))
1111 else:
1112 yield from openmano_nsr.add_vnfr(vnfr)
1113
1114 # Mark the VNFR as running
1115 # TODO: Create a task to monitor nsr/vnfr status
1116 vnfr_msg = vnfr.vnfr_msg.deep_copy()
1117 vnfr_msg.operational_status = "init"
1118
1119 self._log.debug("Attempting to publish openmano vnf: %s", vnfr_msg)
1120 yield from self._publisher.publish_vnfr(None, vnfr_msg)
1121
1122 def update_vnfr(self, vnfr):
1123 vnfr_msg = vnfr.vnfr_msg.deep_copy()
1124 self._log.debug("Attempting to publish openmano vnf: %s", vnfr_msg)
1125 yield from self._publisher.publish_vnfr(None, vnfr_msg)
1126
1127 @asyncio.coroutine
1128 def instantiate_vl(self, nsr, vlr):
1129 """
1130 Instantiate NSR with the passed nsr id
1131 """
1132 self._log.debug("Received instantiate VL for NSR {}; VLR {}".format(nsr.id,vlr))
1133 openmano_nsr = self._openmano_nsrs[nsr.id]
1134 if openmano_nsr._state == OpenmanoNSRecordState.RUNNING:
1135 yield from openmano_nsr.create_vlr(vlr)
1136 yield from self._publisher.publish_vlr(None, vlr.vlr_msg)
1137 else:
1138 yield from openmano_nsr.add_vlr(vlr)
1139
1140 @asyncio.coroutine
1141 def terminate_ns(self, nsr):
1142 """
1143 Terminate the network service
1144 """
1145 nsr_id = nsr.id
1146 openmano_nsr = self._openmano_nsrs[nsr_id]
1147
1148 for _,handler in openmano_nsr._vdur_console_handler.items():
1149 handler._regh.deregister()
1150
1151 yield from self._loop.run_in_executor(
1152 None,
1153 self.terminate,
1154 openmano_nsr,
1155 )
1156
1157 for vnfr in openmano_nsr.vnfrs:
1158 self._log.debug("Unpublishing VNFR: %s", vnfr.vnfr.vnfr_msg)
1159 yield from self._publisher.unpublish_vnfr(None, vnfr.vnfr.vnfr_msg)
1160
1161 del self._openmano_nsrs[nsr_id]
1162
1163 def terminate(self, openmano_nsr):
1164 openmano_nsr.terminate()
1165 openmano_nsr.delete()
1166
1167 @asyncio.coroutine
1168 def terminate_vnf(self, nsr, vnfr, scalein=False):
1169 """
1170 Terminate the network service
1171 """
1172 if scalein:
1173 openmano_vnf_nsr = self._openmano_nsr_by_vnfr_id[vnfr.id]
1174 openmano_vnf_nsr.terminate()
1175 openmano_vnf_nsr.delete()
1176 yield from openmano_vnf_nsr.remove_vnf(vnfr)
1177
1178 @asyncio.coroutine
1179 def terminate_vl(self, vlr):
1180 """
1181 Terminate the virtual link
1182 """
1183 self._log.debug("Received terminate VL for VLR {}".format(vlr))
1184 openmano_nsr = self._openmano_nsrs[vlr._nsr_id]
1185 if openmano_nsr._state == OpenmanoNSRecordState.RUNNING:
1186 yield from openmano_nsr.delete_vlr(vlr)
1187 else:
1188 yield from openmano_nsr.remove_vlr(vlr)