3 # Copyright 2016 RIFT.IO Inc
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
25 gi
.require_version('RwDts', '1.0')
26 gi
.require_version('RwVnfrYang', '1.0')
27 from gi
.repository
import (
32 import rift
.openmano
.rift2openmano
as rift2openmano
33 import rift
.openmano
.openmano_client
as openmano_client
34 from . import rwnsmplugin
38 if sys
.version_info
< (3, 4, 4):
39 asyncio
.ensure_future
= asyncio
.async
42 DUMP_OPENMANO_DIR
= os
.path
.join(
43 os
.environ
["RIFT_ARTIFACTS"],
44 "openmano_descriptors"
48 def dump_openmano_descriptor(name
, descriptor_str
):
49 filename
= "{}_{}.yaml".format(
50 time
.strftime("%Y%m%d-%H%M%S"),
54 filepath
= os
.path
.join(
60 if not os
.path
.exists(DUMP_OPENMANO_DIR
):
61 os
.makedirs(DUMP_OPENMANO_DIR
)
63 with
open(filepath
, 'w') as hdl
:
64 hdl
.write(descriptor_str
)
67 print("Failed to dump openmano descriptor: %s" % str(e
))
71 class VnfrConsoleOperdataDtsHandler(object):
72 """ registers 'D,/vnfr:vnfr-console/vnfr:vnfr[id]/vdur[id]' and handles CRUD from DTS"""
74 def vnfr_vdu_console_xpath(self
):
75 """ path for resource-mgr"""
76 return ("D,/rw-vnfr:vnfr-console/rw-vnfr:vnfr[rw-vnfr:id='{}']/rw-vnfr:vdur[vnfr:id='{}']".format(self
._vnfr
_id
,self
._vdur
_id
))
78 def __init__(self
, dts
, log
, loop
, nsr
, vnfr_id
, vdur_id
, vdu_id
):
85 self
._vnfr
_id
= vnfr_id
86 self
._vdur
_id
= vdur_id
91 """ Register for VNFR VDU Operational Data read from dts """
94 def on_prepare(xact_info
, action
, ks_path
, msg
):
95 """ prepare callback from dts """
96 xpath
= ks_path
.to_xpath(RwVnfrYang
.get_schema())
98 "Got VNFR VDU Opdata xact_info: %s, action: %s): %s:%s",
99 xact_info
, action
, xpath
, msg
102 if action
== rwdts
.QueryAction
.READ
:
103 schema
= RwVnfrYang
.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur
.schema()
104 path_entry
= schema
.keyspec_to_entry(ks_path
)
105 self
._log
.debug("VDU Opdata path is {}".format(path_entry
))
108 console_url
= yield from self
._loop
.run_in_executor(
110 self
._nsr
._http
_api
.get_instance_vm_console_url
,
115 self
._log
.debug("Got console response: %s for NSR ID %s vdur ID %s",
120 vdur_console
= RwVnfrYang
.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur()
121 vdur_console
.id = self
._vdur
_id
123 vdur_console
.console_url
= console_url
125 vdur_console
.console_url
= 'none'
126 self
._log
.debug("Recevied console URL for vdu {} is {}".format(self
._vdu
_id
,vdur_console
))
127 except openmano_client
.InstanceStatusError
as e
:
128 self
._log
.error("Could not get NS instance console URL: %s",
130 vdur_console
= RwVnfrYang
.YangData_RwVnfr_VnfrConsole_Vnfr_Vdur()
131 vdur_console
.id = self
._vdur
_id
132 vdur_console
.console_url
= 'none'
134 xact_info
.respond_xpath(rsp_code
=rwdts
.XactRspCode
.ACK
,
135 xpath
=self
.vnfr_vdu_console_xpath
,
138 #raise VnfRecordError("Not supported operation %s" % action)
139 self
._log
.error("Not supported operation %s" % action
)
140 xact_info
.respond_xpath(rsp_code
=rwdts
.XactRspCode
.ACK
)
143 self
._log
.debug("Registering for VNFR VDU using xpath: %s",
144 self
.vnfr_vdu_console_xpath
)
145 hdl
= rift
.tasklets
.DTS
.RegistrationHandler(on_prepare
=on_prepare
,)
146 with self
._dts
.group_create() as group
:
147 self
._regh
= group
.register(xpath
=self
.vnfr_vdu_console_xpath
,
149 flags
=rwdts
.Flag
.PUBLISHER
,
154 class OpenmanoVnfr(object):
155 def __init__(self
, log
, loop
, cli_api
, vnfr
):
158 self
._cli
_api
= cli_api
160 self
._vnfd
_id
= vnfr
.vnfd
.id
164 self
._created
= False
168 return rift2openmano
.RiftVNFD(self
._vnfr
.vnfd
)
175 def rift_vnfd_id(self
):
179 def openmano_vnfd_id(self
):
183 def openmano_vnfd(self
):
184 self
._log
.debug("Converting vnfd %s from rift to openmano", self
.vnfd
.id)
185 openmano_vnfd
= rift2openmano
.rift2openmano_vnfd(self
.vnfd
)
189 def openmano_vnfd_yaml(self
):
190 return yaml
.safe_dump(self
.openmano_vnfd
, default_flow_style
=False)
194 self
._log
.debug("Creating openmano vnfd")
195 openmano_vnfd
= self
.openmano_vnfd
196 name
= openmano_vnfd
["vnf"]["name"]
198 # If the name already exists, get the openmano vnfd id
199 name_uuid_map
= yield from self
._loop
.run_in_executor(
201 self
._cli
_api
.vnf_list
,
204 if name
in name_uuid_map
:
205 vnf_id
= name_uuid_map
[name
]
206 self
._log
.debug("Vnf already created. Got existing openmano vnfd id: %s", vnf_id
)
207 self
._vnf
_id
= vnf_id
210 self
._vnf
_id
, _
= yield from self
._loop
.run_in_executor(
212 self
._cli
_api
.vnf_create
,
213 self
.openmano_vnfd_yaml
,
216 fpath
= dump_openmano_descriptor(
217 "{}_vnf".format(name
),
218 self
.openmano_vnfd_yaml
221 self
._log
.debug("Dumped Openmano VNF descriptor to: %s", fpath
)
227 if not self
._created
:
230 self
._log
.debug("Deleting openmano vnfd")
231 if self
._vnf
_id
is None:
232 self
._log
.warning("Openmano vnf id not set. Cannot delete.")
235 yield from self
._loop
.run_in_executor(
237 self
._cli
_api
.vnf_delete
,
242 class OpenmanoNsr(object):
245 def __init__(self
, dts
, log
, loop
, publisher
, cli_api
, http_api
, nsd_msg
, nsr_config_msg
):
249 self
._publisher
= publisher
250 self
._cli
_api
= cli_api
251 self
._http
_api
= http_api
253 self
._nsd
_msg
= nsd_msg
254 self
._nsr
_config
_msg
= nsr_config_msg
257 self
._vdur
_console
_handler
= {}
259 self
._nsd
_uuid
= None
260 self
._nsr
_uuid
= None
262 self
._created
= False
264 self
._monitor
_task
= None
268 return rift2openmano
.RiftNSD(self
._nsd
_msg
)
272 return {v
.rift_vnfd_id
: v
.vnfd
for v
in self
._vnfrs
}
279 def openmano_nsd_yaml(self
):
280 self
._log
.debug("Converting nsd %s from rift to openmano", self
.nsd
.id)
281 openmano_nsd
= rift2openmano
.rift2openmano_nsd(self
.nsd
, self
.vnfds
)
282 return yaml
.safe_dump(openmano_nsd
, default_flow_style
=False)
286 def openmano_instance_create_yaml(self
):
287 self
._log
.debug("Creating instance-scenario-create input file for nsd %s with name %s", self
.nsd
.id, self
._nsr
_config
_msg
.name
)
288 openmano_instance_create
= {}
289 openmano_instance_create
["name"] = self
._nsr
_config
_msg
.name
290 openmano_instance_create
["description"] = self
._nsr
_config
_msg
.description
291 openmano_instance_create
["scenario"] = self
._nsd
_uuid
292 if self
._nsr
_config
_msg
.has_field("om_datacenter"):
293 openmano_instance_create
["datacenter"] = self
._nsr
_config
_msg
.om_datacenter
294 openmano_instance_create
["networks"] = {}
295 for vld_msg
in self
._nsd
_msg
.vld
:
296 if vld_msg
.vim_network_name
:
298 network
["name"] = vld_msg
.name
299 network
["netmap-use"] = vld_msg
.vim_network_name
300 #network["datacenter"] = vld_msg.om_datacenter
301 openmano_instance_create
["networks"][vld_msg
.name
] = network
303 return yaml
.safe_dump(openmano_instance_create
, default_flow_style
=False)
307 def add_vnfr(self
, vnfr
):
308 vnfr
= OpenmanoVnfr(self
._log
, self
._loop
, self
._cli
_api
, vnfr
)
309 yield from vnfr
.create()
310 self
._vnfrs
.append(vnfr
)
314 if not self
._created
:
315 self
._log
.debug("NSD wasn't created. Skipping delete.")
318 self
._log
.debug("Deleting openmano nsr")
320 yield from self
._loop
.run_in_executor(
322 self
._cli
_api
.ns_delete
,
326 self
._log
.debug("Deleting openmano vnfrs")
327 for vnfr
in self
._vnfrs
:
328 yield from vnfr
.delete()
332 self
._log
.debug("Creating openmano scenario")
333 name_uuid_map
= yield from self
._loop
.run_in_executor(
335 self
._cli
_api
.ns_list
,
338 if self
._nsd
_msg
.name
in name_uuid_map
:
339 self
._log
.debug("Found existing openmano scenario")
340 self
._nsd
_uuid
= name_uuid_map
[self
._nsd
_msg
.name
]
344 # Use the nsd uuid as the scenario name to rebind to existing
345 # scenario on reload or to support muliple instances of the name
347 self
._nsd
_uuid
, _
= yield from self
._loop
.run_in_executor(
349 self
._cli
_api
.ns_create
,
350 self
.openmano_nsd_yaml
,
353 fpath
= dump_openmano_descriptor(
354 "{}_nsd".format(self
._nsd
_msg
.name
),
355 self
.openmano_nsd_yaml
,
358 self
._log
.debug("Dumped Openmano NS descriptor to: %s", fpath
)
363 def instance_monitor_task(self
):
364 self
._log
.debug("Starting Instance monitoring task")
366 start_time
= time
.time()
370 yield from asyncio
.sleep(1, loop
=self
._loop
)
373 instance_resp_json
= yield from self
._loop
.run_in_executor(
375 self
._http
_api
.get_instance
,
379 self
._log
.debug("Got instance response: %s for NSR ID %s",
383 except openmano_client
.InstanceStatusError
as e
:
384 self
._log
.error("Could not get NS instance status: %s", str(e
))
387 def all_vms_active(vnf
):
388 for vm
in vnf
["vms"]:
389 vm_status
= vm
["status"]
391 if vm_status
!= "ACTIVE":
392 self
._log
.debug("VM is not yet active: %s (status: %s)", vm_uuid
, vm_status
)
397 def any_vm_active_nomgmtip(vnf
):
398 for vm
in vnf
["vms"]:
399 vm_status
= vm
["status"]
401 if vm_status
!= "ACTIVE":
402 self
._log
.debug("VM is not yet active: %s (status: %s)", vm_uuid
, vm_status
)
407 def any_vms_error(vnf
):
408 for vm
in vnf
["vms"]:
409 vm_status
= vm
["status"]
410 vm_vim_info
= vm
["vim_info"]
412 if vm_status
== "ERROR":
413 self
._log
.error("VM Error: %s (vim_info: %s)", vm_uuid
, vm_vim_info
)
418 def get_vnf_ip_address(vnf
):
419 if "ip_address" in vnf
:
420 return vnf
["ip_address"].strip()
423 def get_ext_cp_info(vnf
):
425 for vm
in vnf
["vms"]:
426 if "interfaces" not in vm
:
429 for intf
in vm
["interfaces"]:
430 if "external_name" not in intf
:
433 if not intf
["external_name"]:
436 ip_address
= intf
["ip_address"]
437 if ip_address
is None:
438 ip_address
= "0.0.0.0"
440 cp_info_list
.append((intf
["external_name"], ip_address
))
444 def get_vnf_status(vnfr
):
445 # When we create an openmano descriptor we use <name>__<idx>
446 # to come up with openmano constituent VNF name. Use this
447 # knowledge to map the vnfr back.
448 openmano_vnfr_suffix
= "__{}".format(
449 vnfr
.vnfr
.vnfr_msg
.member_vnf_index_ref
452 for vnf
in instance_resp_json
["vnfs"]:
453 if vnf
["vnf_name"].endswith(openmano_vnfr_suffix
):
456 self
._log
.warning("Could not find vnf status with name that ends with: %s",
457 openmano_vnfr_suffix
)
460 for vnfr
in self
._vnfrs
:
461 if vnfr
in active_vnfs
:
462 # Skipping, so we don't re-publish the same VNF message.
465 vnfr_msg
= vnfr
.vnfr
.vnfr_msg
.deep_copy()
466 vnfr_msg
.operational_status
= "init"
469 vnf_status
= get_vnf_status(vnfr
)
470 self
._log
.debug("Found VNF status: %s", vnf_status
)
471 if vnf_status
is None:
472 self
._log
.error("Could not find VNF status from openmano")
473 vnfr_msg
.operational_status
= "failed"
474 yield from self
._publisher
.publish_vnfr(None, vnfr_msg
)
477 # If there was a VNF that has a errored VM, then just fail the VNF and stop monitoring.
478 if any_vms_error(vnf_status
):
479 self
._log
.debug("VM was found to be in error state. Marking as failed.")
480 vnfr_msg
.operational_status
= "failed"
481 yield from self
._publisher
.publish_vnfr(None, vnfr_msg
)
484 if all_vms_active(vnf_status
):
485 vnf_ip_address
= get_vnf_ip_address(vnf_status
)
487 if vnf_ip_address
is None:
488 self
._log
.warning("No IP address obtained "
489 "for VNF: {}, will retry.".format(
490 vnf_status
['vnf_name']))
493 self
._log
.debug("All VMs in VNF are active. Marking as running.")
494 vnfr_msg
.operational_status
= "running"
496 self
._log
.debug("Got VNF ip address: %s", vnf_ip_address
)
497 vnfr_msg
.mgmt_interface
.ip_address
= vnf_ip_address
498 vnfr_msg
.vnf_configuration
.config_access
.mgmt_ip_address
= vnf_ip_address
501 for vm
in vnf_status
["vms"]:
502 if vm
["uuid"] not in self
._vdur
_console
_handler
:
503 vdur_console_handler
= VnfrConsoleOperdataDtsHandler(self
._dts
, self
._log
, self
._loop
,
504 self
, vnfr_msg
.id,vm
["uuid"],vm
["name"])
505 yield from vdur_console_handler
.register()
506 self
._vdur
_console
_handler
[vm
["uuid"]] = vdur_console_handler
508 vdur_msg
= vnfr_msg
.vdur
.add()
509 vdur_msg
.vim_id
= vm
["vim_vm_id"]
510 vdur_msg
.id = vm
["uuid"]
512 # Add connection point information for the config manager
513 cp_info_list
= get_ext_cp_info(vnf_status
)
514 for (cp_name
, cp_ip
) in cp_info_list
:
515 cp
= vnfr_msg
.connection_point
.add()
517 cp
.short_name
= cp_name
518 cp
.ip_address
= cp_ip
520 yield from self
._publisher
.publish_vnfr(None, vnfr_msg
)
521 active_vnfs
.append(vnfr
)
523 if (time
.time() - start_time
) > OpenmanoNsr
.TIMEOUT_SECS
:
524 self
._log
.error("NSR timed out before reaching running state")
525 vnfr_msg
.operational_status
= "failed"
526 yield from self
._publisher
.publish_vnfr(None, vnfr_msg
)
529 except Exception as e
:
530 vnfr_msg
.operational_status
= "failed"
531 yield from self
._publisher
.publish_vnfr(None, vnfr_msg
)
532 self
._log
.exception("Caught exception publishing vnfr info: %s", str(e
))
535 if len(active_vnfs
) == len(self
._vnfrs
):
536 self
._log
.info("All VNF's are active. Exiting NSR monitoring task")
541 if self
._nsd
_uuid
is None:
542 raise ValueError("Cannot deploy an uncreated nsd")
544 self
._log
.debug("Deploying openmano scenario")
546 name_uuid_map
= yield from self
._loop
.run_in_executor(
548 self
._cli
_api
.ns_instance_list
,
551 if self
._nsr
_config
_msg
.name
in name_uuid_map
:
552 self
._log
.debug("Found existing instance with nsr name: %s", self
._nsr
_config
_msg
.name
)
553 self
._nsr
_uuid
= name_uuid_map
[self
._nsr
_config
_msg
.name
]
555 self
._nsr
_uuid
= yield from self
._loop
.run_in_executor(
557 self
._cli
_api
.ns_instance_scenario_create
,
558 self
.openmano_instance_create_yaml
)
560 fpath
= dump_openmano_descriptor(
561 "{}_instance_sce_create".format(self
._nsr
_config
_msg
.name
),
562 self
.openmano_instance_create_yaml
,
565 self
._log
.debug("Dumped Openmano NS Scenario Cretae to: %s", fpath
)
568 self
._monitor
_task
= asyncio
.ensure_future(
569 self
.instance_monitor_task(), loop
=self
._loop
575 for _
,handler
in self
._vdur
_console
_handler
.items():
576 handler
._regh
.deregister()
578 if self
._nsr
_uuid
is None:
579 self
._log
.warning("Cannot terminate an un-instantiated nsr")
582 if self
._monitor
_task
is not None:
583 self
._monitor
_task
.cancel()
584 self
._monitor
_task
= None
586 self
._log
.debug("Terminating openmano nsr")
587 yield from self
._loop
.run_in_executor(
589 self
._cli
_api
.ns_terminate
,
594 class OpenmanoNsPlugin(rwnsmplugin
.NsmPluginBase
):
596 RW Implentation of the NsmPluginBase
598 def __init__(self
, dts
, log
, loop
, publisher
, ro_account
):
602 self
._publisher
= publisher
605 self
._http
_api
= None
606 self
._openmano
_nsrs
= {}
608 self
._set
_ro
_account
(ro_account
)
610 def _set_ro_account(self
, ro_account
):
611 self
._log
.debug("Setting openmano plugin cloud account: %s", ro_account
)
612 self
._cli
_api
= openmano_client
.OpenmanoCliAPI(
614 ro_account
.openmano
.host
,
615 ro_account
.openmano
.port
,
616 ro_account
.openmano
.tenant_id
,
619 self
._http
_api
= openmano_client
.OpenmanoHttpAPI(
621 ro_account
.openmano
.host
,
622 ro_account
.openmano
.port
,
623 ro_account
.openmano
.tenant_id
,
626 def create_nsr(self
, nsr_config_msg
, nsd_msg
):
628 Create Network service record
630 openmano_nsr
= OpenmanoNsr(
640 self
._openmano
_nsrs
[nsr_config_msg
.id] = openmano_nsr
643 def deploy(self
, nsr_msg
):
644 openmano_nsr
= self
._openmano
_nsrs
[nsr_msg
.ns_instance_config_ref
]
645 yield from openmano_nsr
.create()
646 yield from openmano_nsr
.deploy()
649 def instantiate_ns(self
, nsr
, xact
):
651 Instantiate NSR with the passed nsr id
653 yield from nsr
.instantiate(xact
)
656 def instantiate_vnf(self
, nsr
, vnfr
):
658 Instantiate NSR with the passed nsr id
660 openmano_nsr
= self
._openmano
_nsrs
[nsr
.id]
661 yield from openmano_nsr
.add_vnfr(vnfr
)
663 # Mark the VNFR as running
664 # TODO: Create a task to monitor nsr/vnfr status
665 vnfr_msg
= vnfr
.vnfr_msg
.deep_copy()
666 vnfr_msg
.operational_status
= "init"
668 self
._log
.debug("Attempting to publish openmano vnf: %s", vnfr_msg
)
669 with self
._dts
.transaction() as xact
:
670 yield from self
._publisher
.publish_vnfr(xact
, vnfr_msg
)
673 def instantiate_vl(self
, nsr
, vlr
):
675 Instantiate NSR with the passed nsr id
680 def terminate_ns(self
, nsr
):
682 Terminate the network service
685 openmano_nsr
= self
._openmano
_nsrs
[nsr_id
]
686 yield from openmano_nsr
.terminate()
687 yield from openmano_nsr
.delete()
689 with self
._dts
.transaction() as xact
:
690 for vnfr
in openmano_nsr
.vnfrs
:
691 self
._log
.debug("Unpublishing VNFR: %s", vnfr
.vnfr
.vnfr_msg
)
692 yield from self
._publisher
.unpublish_vnfr(xact
, vnfr
.vnfr
.vnfr_msg
)
694 del self
._openmano
_nsrs
[nsr_id
]
697 def terminate_vnf(self
, vnfr
):
699 Terminate the network service
704 def terminate_vl(self
, vlr
):
706 Terminate the virtual link