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