d6f8483b384f4a62c9896f13273e997301a7f351
[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 try:
396 self._log.debug("Creating instance-scenario-create input file for nsd %s with name %s", self.nsd.id, self._nsr_config_msg.name)
397 openmano_instance_create = {}
398 openmano_instance_create["name"] = self._nsr_config_msg.name
399 openmano_instance_create["description"] = self._nsr_config_msg.description
400 openmano_instance_create["scenario"] = self._nsd_uuid
401
402 cloud_config = self.get_ssh_key_pairs()
403 if cloud_config:
404 openmano_instance_create["cloud-config"] = cloud_config
405 if self._nsr_config_msg.has_field("datacenter"):
406 openmano_instance_create["datacenter"] = self._nsr_config_msg.datacenter
407 openmano_instance_create["vnfs"] = {}
408 for vnfr in self._vnfrs:
409 if "datacenter" in vnfr.vnfr.vnfr_msg:
410 vnfr_name = vnfr.vnfr.vnfd.name + "." + str(vnfr.vnfr.vnfr_msg.member_vnf_index_ref)
411 openmano_instance_create["vnfs"][vnfr_name] = {"datacenter": vnfr.vnfr.vnfr_msg.datacenter}
412 openmano_instance_create["networks"] = {}
413 for vld_msg in self._nsd_msg.vld:
414 openmano_instance_create["networks"][vld_msg.name] = {}
415 openmano_instance_create["networks"][vld_msg.name]["sites"] = list()
416 for vlr in self._vlrs:
417 if vlr.vld_msg.name == vld_msg.name:
418 self._log.debug("Received VLR name %s, VLR DC: %s for VLD: %s",vlr.vld_msg.name,
419 vlr.datacenter_name,vld_msg.name)
420 #network["vim-network-name"] = vld_msg.name
421 network = {}
422 ip_profile = {}
423 if vld_msg.vim_network_name:
424 network["netmap-use"] = vld_msg.vim_network_name
425 elif vlr._ip_profile and vlr._ip_profile.has_field("ip_profile_params"):
426 ip_profile_params = vlr._ip_profile.ip_profile_params
427 if ip_profile_params.ip_version == "ipv6":
428 ip_profile['ip-version'] = "IPv6"
429 else:
430 ip_profile['ip-version'] = "IPv4"
431 if ip_profile_params.has_field('subnet_address'):
432 ip_profile['subnet-address'] = ip_profile_params.subnet_address
433 if ip_profile_params.has_field('gateway_address'):
434 ip_profile['gateway-address'] = ip_profile_params.gateway_address
435 if ip_profile_params.has_field('dns_server') and len(ip_profile_params.dns_server) > 0:
436 ip_profile['dns-address'] = ip_profile_params.dns_server[0].address
437 if ip_profile_params.has_field('dhcp_params'):
438 ip_profile['dhcp'] = {}
439 ip_profile['dhcp']['enabled'] = ip_profile_params.dhcp_params.enabled
440 ip_profile['dhcp']['start-address'] = ip_profile_params.dhcp_params.start_address
441 ip_profile['dhcp']['count'] = ip_profile_params.dhcp_params.count
442 if ip_profile['dhcp']['enabled'] is True and ip_profile['dhcp']['start-address'] is None:
443 addr_pool = list(ipaddress.ip_network(ip_profile['subnet-address']).hosts())
444 gateway_ip_addr = ip_profile.get('gateway-address', None)
445 if gateway_ip_addr is None:
446 gateway_ip_addr = str(next(iter(addr_pool)))
447 ip_profile['gateway-address'] = gateway_ip_addr
448
449 self._log.debug("Gateway Address {}".format(gateway_ip_addr))
450
451 if ipaddress.ip_address(gateway_ip_addr) in addr_pool:
452 addr_pool.remove(ipaddress.ip_address(gateway_ip_addr))
453 if len(addr_pool) > 0:
454 ip_profile['dhcp']['start-address'] = str(next(iter(addr_pool)))
455 #DHCP count more than 200 is not instantiating any instances using OPENMANO RO
456 #So restricting it to a feasible count of 100.
457 dhcp_count = ip_profile['dhcp']['count']
458 if dhcp_count is None or dhcp_count == 0 or dhcp_count > len(addr_pool):
459 ip_profile['dhcp']['count'] = min(len(addr_pool), 100)
460 self._log.debug("DHCP start Address {} DHCP count {}".
461 format(ip_profile['dhcp']['start-address'], ip_profile['dhcp']['count']))
462 else:
463 network["netmap-create"] = vlr.name
464 if vlr.datacenter_name:
465 network["datacenter"] = vlr.datacenter_name
466 elif vld_msg.has_field("datacenter"):
467 network["datacenter"] = vld_msg.datacenter
468 elif "datacenter" in openmano_instance_create:
469 network["datacenter"] = openmano_instance_create["datacenter"]
470 if network:
471 openmano_instance_create["networks"][vld_msg.name]["sites"].append(network)
472 if ip_profile:
473 openmano_instance_create["networks"][vld_msg.name]['ip-profile'] = ip_profile
474 except Exception as e:
475 self._log.error("Error while creating openmano_instance_yaml : {}". format(str(e)))
476
477 return yaml.safe_dump(openmano_instance_create, default_flow_style=False,width=1000)
478
479 @property
480 def scaling_instance_create_yaml(self, scaleout=False):
481 try:
482 self._log.debug("Creating instance-scenario-create input file for nsd %s with name %s", self.nsd.id, self._nsr_config_msg.name+"scal1")
483 scaling_instance_create = {}
484 for group_list in self._nsd_msg.scaling_group_descriptor:
485 scaling_instance_create["name"] = self._nsr_config_msg.name + "__"+group_list.name
486 if scaleout:
487 scaling_instance_create["scenario"] = self._nsd_uuid + "__" +group_list.name
488 else:
489 scaling_instance_create["scenario"] = self._nsd_uuid
490 scaling_instance_create["description"] = self._nsr_config_msg.description
491
492 if self._nsr_config_msg.has_field("datacenter"):
493 scaling_instance_create["datacenter"] = self._nsr_config_msg.datacenter
494 scaling_instance_create["vnfs"] = {}
495 for vnfr in self._vnfrs:
496 if "datacenter" in vnfr.vnfr.vnfr_msg:
497 vnfr_name = vnfr.vnfr.vnfd.name + "." + str(vnfr.vnfr.vnfr_msg.member_vnf_index_ref)
498 scaling_instance_create["vnfs"][vnfr_name] = {"datacenter": self._nsr_config_msg.datacenter}
499 scaling_instance_create["networks"] = {}
500 for vld_msg in self._nsd_msg.vld:
501 scaling_instance_create["networks"][vld_msg.name] = {}
502 scaling_instance_create["networks"][vld_msg.name]["sites"] = list()
503 for vlr in self._vlrs:
504 if vlr.vld_msg.name == vld_msg.name:
505 self._log.debug("Received VLR name %s, VLR DC: %s for VLD: %s",vlr.vld_msg.name,
506 vlr.datacenter_name,vld_msg.name)
507 #network["vim-network-name"] = vld_msg.name
508 network = {}
509 ip_profile = {}
510 if vld_msg.vim_network_name:
511 network["netmap-use"] = vld_msg.vim_network_name
512 #else:
513 # network["netmap-create"] = vlr.name
514 if vlr.datacenter_name:
515 network["datacenter"] = vlr.datacenter_name
516 elif vld_msg.has_field("datacenter"):
517 network["datacenter"] = vld_msg.datacenter
518 elif "datacenter" in scaling_instance_create:
519 network["datacenter"] = scaling_instance_create["datacenter"]
520 if network:
521 scaling_instance_create["networks"][vld_msg.name]["sites"].append(network)
522 except Exception as e:
523 self._log.error("Error while creating scaling_instance_yaml : {}". format(str(e)))
524
525 return yaml.safe_dump(scaling_instance_create, default_flow_style=False, width=1000)
526
527 @asyncio.coroutine
528 def add_vlr(self, vlr):
529 self._vlrs.append(vlr)
530 yield from self._publisher.publish_vlr(None, vlr.vlr_msg)
531 yield from asyncio.sleep(1, loop=self._loop)
532
533 @asyncio.coroutine
534 def remove_vlr(self, vlr):
535 if vlr in self._vlrs:
536 self._vlrs.remove(vlr)
537 yield from self._publisher.unpublish_vlr(None, vlr.vlr_msg)
538 yield from asyncio.sleep(1, loop=self._loop)
539
540 @asyncio.coroutine
541 def remove_vnf(self,vnf):
542 self._log.debug("Unpublishing VNFR - {}".format(vnf))
543
544 delete_vnfr = None
545 for vnfr in self._vnfrs:
546 # Find the vnfr by id
547 if vnfr.vnfr.id == vnf.id:
548 self._log.debug("Found vnfr for delete !")
549 delete_vnfr = vnfr
550 break
551
552 if delete_vnfr:
553 self._log.debug("Removing VNFR : {}".format(delete_vnfr.vnfr.id))
554 self._vnfrs.remove(vnfr)
555 yield from self._publisher.unpublish_vnfr(None, delete_vnfr.vnfr.vnfr_msg)
556 self._log.debug("Removed VNFR : {}".format(delete_vnfr.vnfr.id))
557
558 yield from asyncio.sleep(1, loop=self._loop)
559
560 @asyncio.coroutine
561 def delete_vlr(self, vlr):
562 if vlr in self._vlrs:
563 self._vlrs.remove(vlr)
564 if not vlr.vld_msg.vim_network_name:
565 yield from self._loop.run_in_executor(
566 None,
567 self._cli_api.ns_vim_network_delete,
568 vlr.name,
569 vlr.datacenter_name)
570 yield from self._publisher.unpublish_vlr(None, vlr.vlr_msg)
571 yield from asyncio.sleep(1, loop=self._loop)
572
573 @asyncio.coroutine
574 def add_vnfr(self, vnfr):
575 vnfr = OpenmanoVnfr(self._log, self._loop, self._cli_api, self.http_api,
576 vnfr, nsd=self.nsd, ssh_key=self._ssh_key)
577 yield from vnfr.create()
578 self._vnfrs.append(vnfr)
579
580 @asyncio.coroutine
581 def add_nsr(self, nsr, vnfr):
582 self._nsrs[vnfr.id] = nsr
583
584 def delete(self):
585 if not self._created:
586 self._log.debug("NSD wasn't created. Skipping delete.")
587 return
588
589 self._log.debug("Deleting openmano nsr")
590 # Here we need to check for existing instances using this scenario.
591 # This would exist when we use Scaling Descriptors.
592 # Deleting a scenario before deleting instances results in a orphaned state
593 # TODO: The RO should implement the check done here.
594
595 self._log.debug("Fetching Instance Scenario List before Deleting Scenario")
596
597 instances = self.http_api.instances()
598
599 scenarios_instances = False
600
601 self._log.debug("Fetched Instances List. Checking if scenario is used")
602 for instance in instances:
603 if instance["scenario_id"] == self._nsd_uuid:
604 scenarios_instances = True
605 break
606
607 self._log.debug("Scenario Instances Dependency Exists : %s", scenarios_instances)
608
609 if not scenarios_instances:
610 try:
611 self._cli_api.ns_delete(self._nsd_uuid)
612 except Exception as e:
613 self._log.error(e)
614
615 self._log.debug("Deleting openmano vnfrs(non scaling vnfs)")
616 deleted_vnf_id_list = []
617 for vnfr in self._vnfrs:
618 if vnfr.vnfr.vnfd.id not in deleted_vnf_id_list:
619 vnfr.delete()
620 deleted_vnf_id_list.append(vnfr.vnfr.vnfd.id)
621
622 @asyncio.coroutine
623 def create(self):
624 try:
625 self._log.debug("Created openmano scenario")
626 # The self.openmano_nsd_yaml internally creates the scenario if not found.
627 # Assigning the yaml to a variable so that the api is not fired unnecessarily.
628 nsd_yaml = self.openmano_nsd_yaml
629
630 self._nsd_uuid = yaml.load(nsd_yaml)['uuid']
631 fpath = dump_openmano_descriptor(
632 "{}_nsd".format(self._nsd_msg.name),
633 nsd_yaml,
634 )
635
636 self._log.debug("Dumped Openmano NS descriptor to: %s", fpath)
637
638 self._created = True
639 except Exception as e:
640 self._log.error("Failed to create scenario on Openmano RO : %s", e)
641 raise e
642
643 @asyncio.coroutine
644 def scaling_scenario_create(self):
645 self._log.debug("Creating scaling openmano scenario")
646
647 # The self.openmano_nsd_yaml internally creates the scenario if not found.
648 # Assigning the yaml to a variable so that the api is not fired unnecessarily.
649 nsd_yaml = self.openmano_scaling_yaml
650
651 self._nsd_uuid = yaml.load(nsd_yaml)['uuid']
652
653 fpath = dump_openmano_descriptor(
654 "{}_sgd".format(self._nsd_msg.name),
655 self.scaling_instance_create_yaml,
656 )
657 self._created = True
658
659
660 @asyncio.coroutine
661 def get_nsr_opdata(self):
662 """ NSR opdata associated with this VNFR """
663 xpath = self._project.add_project(
664 "D,/nsr:ns-instance-opdata/nsr:nsr" \
665 "[nsr:ns-instance-config-ref={}]". \
666 format(quoted_key(self.nsr_config_msg.id)))
667
668 results = yield from self._dts.query_read(xpath, rwdts.XactFlag.MERGE)
669
670 for result in results:
671 entry = yield from result
672 nsr_op = entry.result
673 return nsr_op
674
675 return None
676
677
678 @asyncio.coroutine
679 def instance_monitor_task(self):
680 self._log.debug("Starting Instance monitoring task")
681
682 start_time = time.time()
683 active_vnfs = []
684 nsr = yield from self.get_nsr_opdata()
685 while True:
686 active_vms = 0
687 active_nets = 0
688
689 yield from asyncio.sleep(1, loop=self._loop)
690
691 try:
692 instance_resp_json = yield from self._loop.run_in_executor(
693 None,
694 self._http_api.get_instance,
695 self._nsr_uuid,
696 )
697
698 self._log.debug("Got instance response: %s for NSR ID %s",
699 instance_resp_json,
700 self._nsr_uuid)
701
702 for vnf in instance_resp_json['vnfs']:
703 for vm in vnf['vms']:
704 if vm['status'] == 'ACTIVE':
705 active_vms += 1
706 for net in instance_resp_json['nets']:
707 if net['status'] == 'ACTIVE':
708 active_nets += 1
709
710 nsr.orchestration_progress.vms.active = active_vms
711 nsr.orchestration_progress.networks.active = active_nets
712
713 # This is for accesibility of the status from nsm when the control goes back.
714 self._active_vms = active_vms
715 self._active_nets = active_nets
716
717 yield from self._publisher.publish_nsr_opdata(None, nsr)
718
719 except openmano_client.InstanceStatusError as e:
720 self._log.error("Could not get NS instance status: %s", str(e))
721 continue
722
723
724 def all_vms_active(vnf):
725 for vm in vnf["vms"]:
726 vm_status = vm["status"]
727 vm_uuid = vm["uuid"]
728 if vm_status != "ACTIVE":
729 self._log.debug("VM is not yet active: %s (status: %s)", vm_uuid, vm_status)
730 return False
731
732 return True
733
734 def any_vm_active_nomgmtip(vnf):
735 for vm in vnf["vms"]:
736 vm_status = vm["status"]
737 vm_uuid = vm["uuid"]
738 if vm_status != "ACTIVE":
739 self._log.debug("VM is not yet active: %s (status: %s)", vm_uuid, vm_status)
740 return False
741
742 return True
743
744 def any_vms_error(vnf):
745 for vm in vnf["vms"]:
746 vm_status = vm["status"]
747 vm_vim_info = vm["vim_info"]
748 vm_uuid = vm["uuid"]
749 if "ERROR" in vm_status:
750 self._log.error("VM Error: %s (vim_info: %s)", vm_uuid, vm_vim_info)
751 return True, vm['error_msg']
752
753 return False, ''
754
755 def get_vnf_ip_address(vnf):
756 if "ip_address" in vnf:
757 return vnf["ip_address"].strip()
758
759 else:
760 cp_info_list = get_ext_cp_info(vnf)
761
762 for cp_name, ip, mac in cp_info_list:
763 for vld in self.nsd.vlds:
764 if not vld.mgmt_network:
765 continue
766
767 for vld_cp in vld.vnfd_connection_point_ref:
768 if vld_cp.vnfd_connection_point_ref == cp_name:
769 return ip
770 return None
771
772 def get_vnf_mac_address(vnf):
773 if "mac_address" in vnf:
774 return vnf["mac_address"].strip()
775 return None
776
777 def get_ext_cp_info(vnf):
778 cp_info_list = []
779 for vm in vnf["vms"]:
780 if "interfaces" not in vm:
781 continue
782
783 for intf in vm["interfaces"]:
784 if "external_name" not in intf:
785 continue
786
787 if not intf["external_name"]:
788 continue
789
790 ip_address = intf["ip_address"]
791 if ip_address is None:
792 ip_address = "0.0.0.0"
793
794 mac_address = intf["mac_address"]
795 if mac_address is None:
796 mac_address="00:00:00:00:00:00"
797
798 cp_info_list.append((intf["external_name"], ip_address, mac_address))
799
800 return cp_info_list
801
802 def get_vnf_status(vnfr):
803 # When we create an openmano descriptor we use <name>.<idx>
804 # to come up with openmano constituent VNF name. Use this
805 # knowledge to map the vnfr back.
806 openmano_vnfr_suffix = ".{}".format(
807 vnfr.vnfr.vnfr_msg.member_vnf_index_ref
808 )
809
810 for vnf in instance_resp_json["vnfs"]:
811 if vnf["vnf_name"].endswith(openmano_vnfr_suffix):
812 return vnf
813
814 self._log.warning("Could not find vnf status with name that ends with: %s",
815 openmano_vnfr_suffix)
816 return None
817
818 for vnfr in self._vnfrs:
819 if vnfr in active_vnfs:
820 # Skipping, so we don't re-publish the same VNF message.
821 continue
822
823 vnfr_msg = vnfr.vnfr.vnfr_msg.deep_copy()
824 vnfr_msg.operational_status = "init"
825
826 try:
827 vnf_status = get_vnf_status(vnfr)
828 self._log.debug("Found VNF status: %s", vnf_status)
829 if vnf_status is None:
830 self._log.error("Could not find VNF status from openmano")
831 self._state = OpenmanoNSRecordState.FAILED
832 vnfr_msg.operational_status = "failed"
833 yield from self._publisher.publish_vnfr(None, vnfr_msg)
834 return
835
836 # If there was a VNF that has a errored VM, then just fail the VNF and stop monitoring.
837 vm_error, vm_err_msg = any_vms_error(vnf_status)
838 if vm_error:
839 self._log.error("VM was found to be in error state. Marking as failed.")
840 self._state = OpenmanoNSRecordState.FAILED
841 vnfr_msg.operational_status = "failed"
842 vnfr_msg.operational_status_details = vm_err_msg
843 yield from self._publisher.publish_vnfr(None, vnfr_msg)
844 return
845
846 if (time.time() - start_time) > OpenmanoNsr.TIMEOUT_SECS:
847 self._log.error("NSR timed out before reaching running state")
848 self._state = OpenmanoNSRecordState.FAILED
849 vnfr_msg.operational_status = "failed"
850 yield from self._publisher.publish_vnfr(None, vnfr_msg)
851 return
852
853 if all_vms_active(vnf_status):
854 vnf_ip_address = get_vnf_ip_address(vnf_status)
855 vnf_mac_address = get_vnf_mac_address(vnf_status)
856
857 if vnf_ip_address is None:
858 self._log.error("No IP address obtained "
859 "for VNF: {}, will retry.".format(
860 vnf_status['vnf_name']))
861 continue
862
863 self._log.debug("All VMs in VNF are active. Marking as running.")
864 vnfr_msg.operational_status = "running"
865
866 self._log.debug("Got VNF ip address: %s, mac-address: %s",
867 vnf_ip_address, vnf_mac_address)
868 vnfr_msg.mgmt_interface.ip_address = vnf_ip_address
869
870 if vnfr._ssh_key:
871 vnfr_msg.mgmt_interface.ssh_key.public_key = \
872 vnfr._ssh_key['public_key']
873 vnfr_msg.mgmt_interface.ssh_key.private_key_file = \
874 vnfr._ssh_key['private_key']
875
876 for vm in vnf_status["vms"]:
877 if vm["uuid"] not in self._vdur_console_handler:
878 vdur_console_handler = VnfrConsoleOperdataDtsHandler(self._project, self._dts, self._log, self._loop,
879 self, vnfr_msg.id,vm["uuid"],vm["name"])
880 yield from vdur_console_handler.register()
881 self._vdur_console_handler[vm["uuid"]] = vdur_console_handler
882
883 vdur_msg = vnfr_msg.vdur.add()
884 vdur_msg.vim_id = vm["vim_vm_id"]
885 vdur_msg.id = vm["uuid"]
886
887 # Add connection point information for the config manager
888 cp_info_list = get_ext_cp_info(vnf_status)
889 for (cp_name, cp_ip, cp_mac_addr) in cp_info_list:
890 cp = vnfr_msg.connection_point.add()
891 cp.name = cp_name
892 cp.short_name = cp_name
893 cp.ip_address = cp_ip
894 cp.mac_address = cp_mac_addr
895
896 yield from self._publisher.publish_vnfr(None, vnfr_msg)
897 active_vnfs.append(vnfr)
898
899 except Exception as e:
900 vnfr_msg.operational_status = "failed"
901 self._state = OpenmanoNSRecordState.FAILED
902 yield from self._publisher.publish_vnfr(None, vnfr_msg)
903 self._log.exception("Caught exception publishing vnfr info: %s", str(e))
904 return
905
906 if len(active_vnfs) == len(self._vnfrs):
907 self._state = OpenmanoNSRecordState.RUNNING
908 self._log.debug("All VNF's are active. Exiting NSR monitoring task")
909 return
910
911 @asyncio.coroutine
912 def deploy(self,nsr_msg):
913 if self._nsd_uuid is None:
914 raise ValueError("Cannot deploy an uncreated nsd")
915
916 self._log.debug("Deploying openmano instance scenario")
917
918 name_uuid_map = yield from self._loop.run_in_executor(
919 None,
920 self._cli_api.ns_instance_list,
921 )
922
923 if self._nsr_config_msg.name in name_uuid_map:
924 self._log.debug("Found existing instance with nsr name: %s", self._nsr_config_msg.name)
925 self._nsr_uuid = name_uuid_map[self._nsr_config_msg.name]
926 else:
927 self._nsr_msg = nsr_msg
928 fpath = dump_openmano_descriptor(
929 "{}_instance_sce_create".format(self._nsr_config_msg.name),
930 self.openmano_instance_create_yaml,
931 )
932 self._log.debug("Dumped Openmano instance Scenario Cretae to: %s", fpath)
933
934 self._nsr_uuid = yield from self._loop.run_in_executor(
935 None,
936 self._cli_api.ns_instance_scenario_create,
937 self.openmano_instance_create_yaml)
938
939 self._state = OpenmanoNSRecordState.INSTANTIATION_PENDING
940
941 self._monitor_task = asyncio.ensure_future(
942 self.instance_monitor_task(), loop=self._loop
943 )
944
945 @asyncio.coroutine
946 def deploy_scaling(self, nsr_msg, rift_vnfd_id):
947 self._log.debug("Deploying Scaling instance scenario")
948 self._nsr_msg = nsr_msg
949 self._rift_vnfd_id = rift_vnfd_id
950 fpath = dump_openmano_descriptor(
951 "{}_scale_instance".format(self._nsr_config_msg.name),
952 self.scaling_instance_create_yaml
953 )
954 self._nsr_uuid = yield from self._loop.run_in_executor(
955 None,
956 self._cli_api.ns_instance_scenario_create,
957 self.scaling_instance_create_yaml)
958
959 self._state = OpenmanoNSRecordState.INSTANTIATION_PENDING
960
961 self._monitor_task = asyncio.ensure_future(
962 self.instance_monitor_task(), loop=self._loop
963 )
964
965 self._state = OpenmanoNSRecordState.INIT
966
967
968 def terminate(self):
969 if self._nsr_uuid is None:
970 start_time = time.time()
971 while ((time.time() - start_time) < OpenmanoNsr.INSTANCE_TERMINATE_TIMEOUT) and (self._nsr_uuid is None):
972 time.sleep(5)
973 self._log.warning("Waiting for nsr to get instatiated")
974 if self._nsr_uuid is None:
975 self._log.warning("Cannot terminate an un-instantiated nsr")
976 return
977
978 if self._monitor_task is not None:
979 self._monitor_task.cancel()
980 self._monitor_task = None
981
982 self._log.debug("Terminating openmano nsr")
983 self._cli_api.ns_terminate(self._nsr_uuid)
984
985 @asyncio.coroutine
986 def create_vlr(self,vlr):
987 self._log.error("Creating openmano vim network VLR name %s, VLR DC: %s",vlr.vld_msg.name,
988 vlr.datacenter_name)
989 net_create = {}
990 net = {}
991 net['name'] = vlr.name
992 net['shared'] = True
993 net['type'] = 'bridge'
994 self._log.error("Received ip profile is %s",vlr._ip_profile)
995 if vlr._ip_profile and vlr._ip_profile.has_field("ip_profile_params"):
996 ip_profile_params = vlr._ip_profile.ip_profile_params
997 ip_profile = {}
998 if ip_profile_params.ip_version == "ipv6":
999 ip_profile['ip_version'] = "IPv6"
1000 else:
1001 ip_profile['ip_version'] = "IPv4"
1002 if ip_profile_params.has_field('subnet_address'):
1003 ip_profile['subnet_address'] = ip_profile_params.subnet_address
1004 if ip_profile_params.has_field('gateway_address'):
1005 ip_profile['gateway_address'] = ip_profile_params.gateway_address
1006 if ip_profile_params.has_field('dns_server') and len(ip_profile_params.dns_server) > 0:
1007 ip_profile['dns_address'] = ip_profile_params.dns_server[0].address
1008 if ip_profile_params.has_field('dhcp_params'):
1009 ip_profile['dhcp_enabled'] = ip_profile_params.dhcp_params.enabled
1010 ip_profile['dhcp_start_address'] = ip_profile_params.dhcp_params.start_address
1011 ip_profile['dhcp_count'] = ip_profile_params.dhcp_params.count
1012 net['ip_profile'] = ip_profile
1013 net_create["network"]= net
1014
1015 net_create_msg = yaml.safe_dump(net_create,default_flow_style=False)
1016 fpath = dump_openmano_descriptor(
1017 "{}_vim_net_create_{}".format(self._nsr_config_msg.name,vlr.name),
1018 net_create_msg)
1019 self._log.error("Dumped Openmano VIM Net create to: %s", fpath)
1020
1021 vim_network_uuid = yield from self._loop.run_in_executor(
1022 None,
1023 self._cli_api.ns_vim_network_create,
1024 net_create_msg,
1025 vlr.datacenter_name)
1026 self._vlrs.append(vlr)
1027
1028
1029
1030 class OpenmanoNsPlugin(nsmpluginbase.NsmPluginBase):
1031 """
1032 RW Implentation of the NsmPluginBase
1033 """
1034 def __init__(self, dts, log, loop, publisher, ro_account, project):
1035 self._dts = dts
1036 self._log = log
1037 self._loop = loop
1038 self._publisher = publisher
1039 self._project = project
1040
1041 self._cli_api = None
1042 self._http_api = None
1043 self._openmano_nsrs = {}
1044 self._openmano_nsr_by_vnfr_id = {}
1045 #self._nsr_uuid = None
1046
1047 self._set_ro_account(ro_account)
1048
1049 def _set_ro_account(self, ro_account):
1050 self._log.debug("Setting openmano plugin cloud account: %s", ro_account)
1051 self._cli_api = openmano_client.OpenmanoCliAPI(
1052 self.log,
1053 ro_account.openmano.host,
1054 ro_account.openmano.port,
1055 ro_account.openmano.tenant_id,
1056 )
1057
1058 self._http_api = openmano_client.OpenmanoHttpAPI(
1059 self.log,
1060 ro_account.openmano.host,
1061 ro_account.openmano.port,
1062 ro_account.openmano.tenant_id,
1063 )
1064
1065 def set_state(self, nsr_id, state):
1066 # Currently we update only during terminate to
1067 # decide how to handle VL terminate
1068 if state.value == OpenmanoNSRecordState.TERMINATE.value:
1069 self._openmano_nsrs[nsr_id]._state = \
1070 [member.value for name, member in \
1071 OpenmanoNSRecordState.__members__.items() \
1072 if member.value == state.value]
1073
1074 def create_nsr(self, nsr_config_msg, nsd_msg, key_pairs=None, ssh_key=None):
1075 """
1076 Create Network service record
1077 """
1078 openmano_nsr = OpenmanoNsr(
1079 self._project,
1080 self._dts,
1081 self._log,
1082 self._loop,
1083 self._publisher,
1084 self._cli_api,
1085 self._http_api,
1086 nsd_msg,
1087 nsr_config_msg,
1088 key_pairs,
1089 ssh_key,
1090 )
1091 self.log.debug("NSR created in openmano nsm %s", openmano_nsr)
1092 self._openmano_nsrs[nsr_config_msg.id] = openmano_nsr
1093
1094 @asyncio.coroutine
1095 def deploy(self, nsr_msg):
1096 self._log.debug("Received NSR Deploy msg : %s", nsr_msg)
1097 openmano_nsr = self._openmano_nsrs[nsr_msg.ns_instance_config_ref]
1098 yield from openmano_nsr.create()
1099 yield from openmano_nsr.deploy(nsr_msg)
1100
1101 @asyncio.coroutine
1102 def instantiate_ns(self, nsr, xact):
1103 """
1104 Instantiate NSR with the passed nsr id
1105 """
1106 yield from nsr.instantiate(xact)
1107
1108 @asyncio.coroutine
1109 def instantiate_vnf(self, nsr, vnfr, scaleout=False):
1110 """
1111 Instantiate NSR with the passed nsr id
1112 """
1113 openmano_nsr = self._openmano_nsrs[nsr.id]
1114 if scaleout:
1115 self._log.debug("Scaleout set as True. Creating Scaling VNFR : {}".format(vnfr))
1116 openmano_vnf_nsr = OpenmanoNsr(
1117 self._project,
1118 self._dts,
1119 self._log,
1120 self._loop,
1121 self._publisher,
1122 self._cli_api,
1123 self._http_api,
1124 openmano_nsr.nsd_msg,
1125 openmano_nsr.nsr_config_msg,
1126 openmano_nsr.key_pairs,
1127 None,
1128 rift_vnfd_id=vnfr.vnfd.id,
1129 )
1130 self._openmano_nsr_by_vnfr_id[nsr.id] = openmano_nsr
1131 if vnfr.id in self._openmano_nsr_by_vnfr_id:
1132 raise VNFExistError("VNF %s already exist", vnfr.id)
1133 self._openmano_nsr_by_vnfr_id[vnfr.id] = openmano_vnf_nsr
1134 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]))
1135 self._log.debug("Inserting VNFR - {}, in NSR - {}".format(vnfr.id, self._openmano_nsr_by_vnfr_id))
1136 for vlr in openmano_nsr.vlrs:
1137 yield from openmano_vnf_nsr.add_vlr(vlr)
1138 try:
1139 yield from openmano_nsr.add_nsr(openmano_vnf_nsr, vnfr)
1140 except Exception as e:
1141 self.log.exception(str(e))
1142 try:
1143 yield from openmano_vnf_nsr.add_vnfr(vnfr)
1144 except Exception as e:
1145 self.log.exception(str(e))
1146 try:
1147 yield from openmano_vnf_nsr.scaling_scenario_create()
1148 except Exception as e:
1149 self.log.exception(str(e))
1150 try:
1151 yield from openmano_vnf_nsr.deploy_scaling(openmano_vnf_nsr.nsr_msg, vnfr.id)
1152 except Exception as e:
1153 self.log.exception(str(e))
1154 else:
1155 self._log.debug("Creating constituent VNFR - {}; for NSR - {}".format(vnfr, nsr))
1156 yield from openmano_nsr.add_vnfr(vnfr)
1157
1158 # Mark the VNFR as running
1159 # TODO: Create a task to monitor nsr/vnfr status
1160 vnfr_msg = vnfr.vnfr_msg.deep_copy()
1161 vnfr_msg.operational_status = "init"
1162
1163 self._log.debug("Attempting to publish openmano vnf: %s", vnfr_msg)
1164 yield from self._publisher.publish_vnfr(None, vnfr_msg)
1165
1166 def update_vnfr(self, vnfr):
1167 vnfr_msg = vnfr.vnfr_msg.deep_copy()
1168 self._log.debug("Attempting to publish openmano vnf: %s", vnfr_msg)
1169 yield from self._publisher.publish_vnfr(None, vnfr_msg)
1170
1171 @asyncio.coroutine
1172 def instantiate_vl(self, nsr, vlr):
1173 """
1174 Instantiate NSR with the passed nsr id
1175 """
1176 self._log.debug("Received instantiate VL for NSR {}; VLR {}".format(nsr.id,vlr))
1177 openmano_nsr = self._openmano_nsrs[nsr.id]
1178 if openmano_nsr._state == OpenmanoNSRecordState.RUNNING:
1179 yield from openmano_nsr.create_vlr(vlr)
1180 yield from self._publisher.publish_vlr(None, vlr.vlr_msg)
1181 else:
1182 yield from openmano_nsr.add_vlr(vlr)
1183
1184 @asyncio.coroutine
1185 def terminate_ns(self, nsr):
1186 """
1187 Terminate the network service
1188 """
1189 self._log.debug("Terminate Received for Openamno NSR - {}".format(nsr))
1190 nsr_id = nsr.id
1191 openmano_nsr = self._openmano_nsrs[nsr_id]
1192
1193 for _,handler in openmano_nsr._vdur_console_handler.items():
1194 handler._regh.deregister()
1195
1196 yield from self._loop.run_in_executor(
1197 None,
1198 self.terminate,
1199 openmano_nsr,
1200 )
1201
1202 for vnfr in openmano_nsr.vnfrs:
1203 self._log.debug("Unpublishing Constituent VNFR: %s", vnfr.vnfr.vnfr_msg)
1204 yield from self._publisher.unpublish_vnfr(None, vnfr.vnfr.vnfr_msg)
1205
1206 del self._openmano_nsrs[nsr_id]
1207
1208 def terminate(self, openmano_nsr):
1209 openmano_nsr.terminate()
1210 openmano_nsr.delete()
1211
1212 @asyncio.coroutine
1213 def terminate_vnf(self, nsr, vnfr, scalein=False):
1214 """
1215 Terminate the network service
1216 """
1217 if scalein:
1218 self._log.debug("Terminating Scaling VNFR - {}".format(vnfr))
1219 openmano_vnf_nsr = self._openmano_nsr_by_vnfr_id[vnfr.id]
1220 openmano_vnf_nsr.terminate()
1221 openmano_vnf_nsr.delete()
1222 yield from openmano_vnf_nsr.remove_vnf(vnfr)
1223
1224 @asyncio.coroutine
1225 def terminate_vl(self, vlr):
1226 """
1227 Terminate the virtual link
1228 """
1229 self._log.debug("Received terminate VL for VLR {}".format(vlr))
1230 openmano_nsr = self._openmano_nsrs[vlr._nsr_id]
1231 if openmano_nsr._state == OpenmanoNSRecordState.RUNNING:
1232 yield from openmano_nsr.delete_vlr(vlr)
1233 else:
1234 yield from openmano_nsr.remove_vlr(vlr)