From: Philip Joseph Date: Fri, 25 Nov 2016 12:36:27 +0000 (-0500) Subject: New Feature : Addtional changes for VNF access point support X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=refs%2Fchanges%2F41%2F741%2F1;p=osm%2FSO.git New Feature : Addtional changes for VNF access point support Signed-off-by: Philip Joseph --- diff --git a/examples/ping_pong_ns/CMakeLists.txt b/examples/ping_pong_ns/CMakeLists.txt index e373c2a2..2dd5b28c 100644 --- a/examples/ping_pong_ns/CMakeLists.txt +++ b/examples/ping_pong_ns/CMakeLists.txt @@ -67,12 +67,17 @@ rift_python_install_tree( COMPONENT ${PKG_LONG_NAME} FILES rift/mano/examples/ping_pong_nsd.py + rift/mano/examples/ping_setup.py + rift/mano/examples/ping_rate.py + rift/mano/examples/ping_start_stop.py + rift/mano/examples/pong_setup.py + rift/mano/examples/pong_start_stop.py rift/mano/examples/start_traffic.py ) install( PROGRAMS - rift/mano/examples/ping_config.py + rift/mano/examples/ping_scale.py stand_up_ping_pong DESTINATION usr/bin COMPONENT ${PKG_LONG_NAME} diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_config.py b/examples/ping_pong_ns/rift/mano/examples/ping_config.py deleted file mode 100755 index 4e5fd35c..00000000 --- a/examples/ping_pong_ns/rift/mano/examples/ping_config.py +++ /dev/null @@ -1,195 +0,0 @@ -#!/usr/bin/env python3 - -# -# Copyright 2016 RIFT.IO Inc -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -import argparse -import logging -import os -import stat -import subprocess -import sys -import time -import yaml - -def ping_config(run_dir, mgmt_ip, mgmt_port, pong_cp, logger, dry_run): - sh_file = "{}/ping_config-{}.sh".format(run_dir, time.strftime("%Y%m%d%H%M%S")) - logger.debug("Creating script file %s" % sh_file) - f = open(sh_file, "w") - f.write(r''' -#!/bin/bash - -# Rest API config -ping_mgmt_ip='{}' -ping_mgmt_port={} - -# VNF specific configuration -pong_server_ip='{}' -ping_rate=5 -server_port=5555 -'''.format(mgmt_ip, mgmt_port, pong_cp)) - - f.write(r''' -# Check if the port is open -DELAY=1 -MAX_TRIES=60 -COUNT=0 -while true; do - COUNT=$(expr $COUNT + 1) - timeout 1 bash -c "cat < /dev/null > /dev/tcp/${ping_mgmt_ip}/${ping_mgmt_port}" - rc=$? - if [ $rc -ne 0 ] - then - echo "Failed to connect to server ${ping_mgmt_ip}:${ping_mgmt_port} for ping with $rc!" - if [ ${COUNT} -gt ${MAX_TRIES} ]; then - exit $rc - fi - sleep ${DELAY} - else - break - fi -done - -# Make rest API calls to configure VNF -curl -D /dev/stdout \ - -H "Accept: application/vnd.yang.data+xml" \ - -H "Content-Type: application/vnd.yang.data+json" \ - -X POST \ - -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \ - http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server -rc=$? -if [ $rc -ne 0 ] -then - echo "Failed to set server info for ping!" - exit $rc -fi - -curl -D /dev/stdout \ - -H "Accept: application/vnd.yang.data+xml" \ - -H "Content-Type: application/vnd.yang.data+json" \ - -X POST \ - -d "{\"rate\":$ping_rate}" \ - http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/rate -rc=$? -if [ $rc -ne 0 ] -then - echo "Failed to set ping rate!" - exit $rc -fi - -output=$(curl -D /dev/stdout \ - -H "Accept: application/vnd.yang.data+xml" \ - -H "Content-Type: application/vnd.yang.data+json" \ - -X POST \ - -d "{\"enable\":true}" \ - http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/adminstatus/state) -if [[ $output == *"Internal Server Error"* ]] -then - echo $output - exit 3 -else - echo $output -fi - -exit 0 -''') - f.close() - os.chmod(sh_file, stat.S_IRWXU) - if not dry_run: - rc = subprocess.call(sh_file, shell=True) - if rc: - logger.error("Config failed: {}".format(rc)) - return False - return True - - - -def main(argv=sys.argv[1:]): - try: - parser = argparse.ArgumentParser() - parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) - parser.add_argument("--dry-run", action="store_true") - parser.add_argument("--quiet", "-q", dest="verbose", action="store_false") - args = parser.parse_args() - - run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") - if not os.path.exists(run_dir): - os.makedirs(run_dir) - log_file = "{}/rift_ping_config-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) - logging.basicConfig(filename=log_file, level=logging.DEBUG) - logger = logging.getLogger() - - ch = logging.StreamHandler() - if args.verbose: - ch.setLevel(logging.DEBUG) - else: - ch.setLevel(logging.INFO) - - # create formatter and add it to the handlers - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - ch.setFormatter(formatter) - logger.addHandler(ch) - - except Exception as e: - print("Got exception:{}".format(e)) - raise e - - try: - dry_run = args.dry_run - - yaml_str = args.yaml_cfg_file.read() - logger.debug("Input YAML file: {}".format(yaml_str)) - yaml_cfg = yaml.load(yaml_str) - logger.debug("Input YAML: {}".format(yaml_cfg)) - - # Check if this is post scale out trigger - if yaml_cfg['trigger'] != 'post_scale_out': - logger.error("Unexpected trigger {}". - format(yaml_cfg['trigger'])) - raise - - pong_cp = "" - for vnfr in yaml_cfg['vnfrs_others']: - # Find the pong VNFR, assuming vnfr name will - # have pong_vnfd as a substring - if 'pong_vnfd' in vnfr['name']: - for cp in vnfr['connection_points']: - logger.debug("Connection point {}".format(cp)) - if 'cp0' in cp['name']: - pong_cp = cp['ip_address'] - break - if not len(pong_cp): - logger.error("Did not get Pong cp0 IP") - raise - - for vnfr in yaml_cfg['vnfrs_in_group']: - mgmt_ip = vnfr['rw_mgmt_ip'] - mgmt_port = vnfr['rw_mgmt_port'] - if ping_config(run_dir, mgmt_ip, mgmt_port, pong_cp, logger, dry_run): - logger.info("Successfully configured Ping {} at {}". - format(vnfr['name'], mgmt_ip)) - else: - logger.error("Config of ping {} with {} failed". - format(vnfr['name'], mgmt_ip)) - raise - - except Exception as e: - logger.error("Got exception {}".format(e)) - logger.exception(e) - raise e - -if __name__ == "__main__": - main() diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py index 919c5b72..8265f459 100755 --- a/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py +++ b/examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py @@ -205,7 +205,7 @@ class VirtualNetworkFunction(ManoDescriptor): # Add vnf configuration vnf_config = vnfd.vnf_configuration - vnf_config.config_attributes.config_delay = 60 + # vnf_config.config_attributes.config_delay = 10 # Select "script" configuration vnf_config.script.script_type = 'bash' @@ -223,7 +223,7 @@ class VirtualNetworkFunction(ManoDescriptor): {"name": "pong_port", "data_type": "INTEGER", "default_value": "5555"}, ], - "user_defined_script": "ping-setup.py", + "user_defined_script": "ping_setup.py", }) vnf_config.config_primitive.append(prim) @@ -237,7 +237,7 @@ class VirtualNetworkFunction(ManoDescriptor): {"name": "rate", "data_type": "INTEGER", "default_value": "5"}, ], - "user_defined_script": "ping-set-rate.py", + "user_defined_script": "ping_rate.py", }) vnf_config.config_primitive.append(prim) @@ -251,7 +251,7 @@ class VirtualNetworkFunction(ManoDescriptor): {"name": "start", "data_type": "BOOLEAN", "default_value": "true"} ], - "user_defined_script": "ping-start-stop.py", + "user_defined_script": "ping_start_stop.py", }) vnf_config.config_primitive.append(prim) @@ -302,7 +302,7 @@ class VirtualNetworkFunction(ManoDescriptor): {"name": "service_ip", "data_type": "STRING"}, {"name": "service_port", "data_type": "INTEGER"}, ], - "user_defined_script": "ping-setup.py", + "user_defined_script": "pong_setup.py", }) vnf_config.config_primitive.append(prim) @@ -316,7 +316,7 @@ class VirtualNetworkFunction(ManoDescriptor): {"name": "start", "data_type": "BOOLEAN", "default_value": "true"} ], - "user_defined_script": "ping-start-stop.py", + "user_defined_script": "pong_start_stop.py", }) vnf_config.config_primitive.append(prim) @@ -519,16 +519,16 @@ class VirtualNetworkFunction(ManoDescriptor): member_vdu.member_vdu_ref = vdu.id - def write_to_file(self, outdir, output_format): + def write_to_file(self, outdir, output_format, use_vca_conf=False): dirpath = "%s/%s" % (outdir, self.name) if not os.path.exists(dirpath): os.makedirs(dirpath) super(VirtualNetworkFunction, self).write_to_file(['vnfd', 'rw-vnfd'], dirpath, output_format) - self.add_scripts(outdir) + self.add_scripts(outdir, use_vca_conf=use_vca_conf) - def add_scripts(self, outdir): + def add_cloud_init(self, outdir): script_dir = os.path.join(outdir, self.name, 'cloud_init') try: os.makedirs(script_dir) @@ -536,7 +536,7 @@ class VirtualNetworkFunction(ManoDescriptor): if not os.path.isdir(script_dir): raise - if 'ping' in self.name: + if 'ping_' in self.name: script_file = os.path.join(script_dir, 'ping_cloud_init.cfg') cfg = PING_USERDATA_FILE else: @@ -546,6 +546,35 @@ class VirtualNetworkFunction(ManoDescriptor): with open(script_file, "w") as f: f.write("{}".format(cfg)) + def add_scripts(self, outdir, use_vca_conf=False): + self.add_cloud_init(outdir) + if use_vca_conf: + self.add_vca_scripts(outdir) + + def add_vca_scripts(self, outdir): + dest_path = os.path.join(outdir, self.name, 'scripts') + try: + os.makedirs(dest_path) + except OSError: + if not os.path.isdir(dest_path): + raise + + if 'pong_' in self.name: + scripts = ['pong_setup.py', 'pong_start_stop.py'] + else: + scripts = ['ping_setup.py', 'ping_rate.py', 'ping_start_stop.py'] + + for script_name in scripts: + src_path = os.path.dirname(os.path.abspath( + os.path.realpath(__file__))) + script_src = os.path.join(src_path, script_name) + if not os.path.exists(script_src): + src_path = os.path.join(os.environ['RIFT_ROOT'], + 'modules/core/mano/examples/' + 'ping_pong_ns/rift/mano/examples') + script_src = os.path.join(src_path, script_name) + + shutil.copy2(script_src, dest_path) class NetworkService(ManoDescriptor): def __init__(self, name): @@ -781,7 +810,7 @@ exit 0 if mano_ut: nsd.service_primitive.add().from_dict( { - "name": "ping config", + "name": "ping scale", "user_defined_script": "{}".format(os.path.join( os.environ['RIFT_ROOT'], 'modules/core/mano', @@ -791,8 +820,8 @@ exit 0 else: nsd.service_primitive.add().from_dict( { - "name": "ping config", - "user_defined_script": "ping_config.py" + "name": "ping scale", + "user_defined_script": "ping_scale.py" }) def ns_initial_config(self, nsd): @@ -849,7 +878,7 @@ exit 0 vnfap_map.capability.member_vnf_index = 2 vnfap_map.capability.capability_ref = 'service_port' vnfap_map.dependency.member_vnf_index = 1 - vnfap_map.dependency.dependency_ref = 'port_port' + vnfap_map.dependency.dependency_ref = 'pong_port' def compose(self, vnfd_list, cpgroup_list, mano_ut, use_ns_init_conf=True, @@ -1078,7 +1107,7 @@ class ScaleGroup(object): def add_config(self): self.config_action['post_scale_out']= {'ns-config-primitive-name-ref': - 'ping config'} + 'ping scale'} class PlacementGroup(object): def __init__(self, name): @@ -1292,8 +1321,10 @@ def generate_ping_pong_descriptors(fmt="json", ) if write_to_file: - ping.write_to_file(out_dir, ping_fmt if ping_fmt is not None else fmt) - pong.write_to_file(out_dir, pong_fmt if ping_fmt is not None else fmt) + ping.write_to_file(out_dir, ping_fmt if ping_fmt is not None else fmt, + use_vca_conf=use_vca_conf) + pong.write_to_file(out_dir, pong_fmt if ping_fmt is not None else fmt, + use_vca_conf=use_vca_conf) nsd_catalog.write_config(out_dir, vnfd_list) nsd_catalog.write_to_file(out_dir, ping_fmt if nsd_fmt is not None else fmt) diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_rate.py b/examples/ping_pong_ns/rift/mano/examples/ping_rate.py new file mode 100755 index 00000000..b3ab07ba --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/ping_rate.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def ping_rate(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18888 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + rate = 1 + if 'rate' in params: + rate = params['rate'] + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"rate\\":{rate}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/ping/rate'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + rate=rate) + + logger.debug("Executing cmd: %s", cmd) + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + return proc.returncode + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_rate-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-rate') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = ping_rate(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_scale.py b/examples/ping_pong_ns/rift/mano/examples/ping_scale.py new file mode 100755 index 00000000..4e5fd35c --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/ping_scale.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 + +# +# Copyright 2016 RIFT.IO Inc +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import logging +import os +import stat +import subprocess +import sys +import time +import yaml + +def ping_config(run_dir, mgmt_ip, mgmt_port, pong_cp, logger, dry_run): + sh_file = "{}/ping_config-{}.sh".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + logger.debug("Creating script file %s" % sh_file) + f = open(sh_file, "w") + f.write(r''' +#!/bin/bash + +# Rest API config +ping_mgmt_ip='{}' +ping_mgmt_port={} + +# VNF specific configuration +pong_server_ip='{}' +ping_rate=5 +server_port=5555 +'''.format(mgmt_ip, mgmt_port, pong_cp)) + + f.write(r''' +# Check if the port is open +DELAY=1 +MAX_TRIES=60 +COUNT=0 +while true; do + COUNT=$(expr $COUNT + 1) + timeout 1 bash -c "cat < /dev/null > /dev/tcp/${ping_mgmt_ip}/${ping_mgmt_port}" + rc=$? + if [ $rc -ne 0 ] + then + echo "Failed to connect to server ${ping_mgmt_ip}:${ping_mgmt_port} for ping with $rc!" + if [ ${COUNT} -gt ${MAX_TRIES} ]; then + exit $rc + fi + sleep ${DELAY} + else + break + fi +done + +# Make rest API calls to configure VNF +curl -D /dev/stdout \ + -H "Accept: application/vnd.yang.data+xml" \ + -H "Content-Type: application/vnd.yang.data+json" \ + -X POST \ + -d "{\"ip\":\"$pong_server_ip\", \"port\":$server_port}" \ + http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/server +rc=$? +if [ $rc -ne 0 ] +then + echo "Failed to set server info for ping!" + exit $rc +fi + +curl -D /dev/stdout \ + -H "Accept: application/vnd.yang.data+xml" \ + -H "Content-Type: application/vnd.yang.data+json" \ + -X POST \ + -d "{\"rate\":$ping_rate}" \ + http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/rate +rc=$? +if [ $rc -ne 0 ] +then + echo "Failed to set ping rate!" + exit $rc +fi + +output=$(curl -D /dev/stdout \ + -H "Accept: application/vnd.yang.data+xml" \ + -H "Content-Type: application/vnd.yang.data+json" \ + -X POST \ + -d "{\"enable\":true}" \ + http://${ping_mgmt_ip}:${ping_mgmt_port}/api/v1/ping/adminstatus/state) +if [[ $output == *"Internal Server Error"* ]] +then + echo $output + exit 3 +else + echo $output +fi + +exit 0 +''') + f.close() + os.chmod(sh_file, stat.S_IRWXU) + if not dry_run: + rc = subprocess.call(sh_file, shell=True) + if rc: + logger.error("Config failed: {}".format(rc)) + return False + return True + + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("--dry-run", action="store_true") + parser.add_argument("--quiet", "-q", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/rift_ping_config-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger() + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + ch.setFormatter(formatter) + logger.addHandler(ch) + + except Exception as e: + print("Got exception:{}".format(e)) + raise e + + try: + dry_run = args.dry_run + + yaml_str = args.yaml_cfg_file.read() + logger.debug("Input YAML file: {}".format(yaml_str)) + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + # Check if this is post scale out trigger + if yaml_cfg['trigger'] != 'post_scale_out': + logger.error("Unexpected trigger {}". + format(yaml_cfg['trigger'])) + raise + + pong_cp = "" + for vnfr in yaml_cfg['vnfrs_others']: + # Find the pong VNFR, assuming vnfr name will + # have pong_vnfd as a substring + if 'pong_vnfd' in vnfr['name']: + for cp in vnfr['connection_points']: + logger.debug("Connection point {}".format(cp)) + if 'cp0' in cp['name']: + pong_cp = cp['ip_address'] + break + if not len(pong_cp): + logger.error("Did not get Pong cp0 IP") + raise + + for vnfr in yaml_cfg['vnfrs_in_group']: + mgmt_ip = vnfr['rw_mgmt_ip'] + mgmt_port = vnfr['rw_mgmt_port'] + if ping_config(run_dir, mgmt_ip, mgmt_port, pong_cp, logger, dry_run): + logger.info("Successfully configured Ping {} at {}". + format(vnfr['name'], mgmt_ip)) + else: + logger.error("Config of ping {} with {} failed". + format(vnfr['name'], mgmt_ip)) + raise + + except Exception as e: + logger.error("Got exception {}".format(e)) + logger.exception(e) + raise e + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_setup.py b/examples/ping_pong_ns/rift/mano/examples/ping_setup.py new file mode 100755 index 00000000..c05e669b --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/ping_setup.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def ping_setup(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18888 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + pong_ip = params['pong_ip'] + pong_port = 5555 + if 'pong_port' in params: + pong_port = params['pong_port'] + rate = 1 + if 'rate' in params: + rate = params['rate'] + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"ip\\":\\"{pong_ip}\\", \\"port\\":{pong_port}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/ping/server'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + pong_ip=pong_ip, + pong_port=pong_port) + + logger.debug("Executing cmd: %s", cmd) + count = 0 + delay = 5 + max_tries = 12 + rc = 0 + + while True: + count += 1 + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + if proc.returncode == 0: + # Check if response is 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' in resp: + rc = 0 + break + self._log.error("Got error response: {}".format(resp)) + rc = 1 + break + + elif proc.returncode == 7: + # Connection timeout + if count >= max_tries: + self._log.error("Connect failed for {}. Failing".format(count)) + rc = 7 + break + # Try after delay + time.sleep(delay) + + return rc + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_setup-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-setup') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = ping_setup(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py b/examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py new file mode 100755 index 00000000..76f653c0 --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def ping_start_stop(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18888 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + start = 'true' + if 'start' in params: + if not params['start']: + start = 'false' + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"enable\\":{start}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/ping/adminstatus/state'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + start=start) + + logger.debug("Executing cmd: %s", cmd) + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + rc = proc.returncode + + if rc == 0: + # Check if we got 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' not in resp: + self._log.error("Got error response: {}".format(resp)) + rc = 1 + + return rc + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/ping_start_stop-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-start-stop') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = ping_start_stop(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/pong_setup.py b/examples/ping_pong_ns/rift/mano/examples/pong_setup.py new file mode 100755 index 00000000..cd56eca8 --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/pong_setup.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def pong_setup(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18889 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + service_ip = params['service_ip'] + service_port = 5555 + if 'service_port' in params: + service_port = params['service_port'] + rate = 1 + if 'rate' in params: + rate = params['rate'] + + config_cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"ip\\":\\"{service_ip}\\", \\"port\\":{service_port}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/pong/server'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + service_ip=service_ip, + service_port=service_port) + + logger.debug("Executing cmd: %s", config_cmd) + count = 0 + delay = 5 + max_tries = 12 + rc = 0 + + while True: + count += 1 + proc = subprocess.run(config_cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + if proc.returncode == 0: + # Check if response is 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' in resp: + rc = 0 + break + self._log.error("Got error response: {}".format(resp)) + rc = 1 + break + + elif proc.returncode == 7: + # Connection timeout + if count >= max_tries: + self._log.error("Connect failed for {}. Failing".format(count)) + rc = 7 + break + # Try after delay + time.sleep(delay) + + return rc + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/pong_setup-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('pong-setup') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = pong_setup(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py b/examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py new file mode 100755 index 00000000..b5195ddf --- /dev/null +++ b/examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 + +############################################################################ +# Copyright 2016 RIFT.IO Inc # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################ + + +import argparse +import logging +import os +import subprocess +import sys +import time + +import yaml + + +def pong_start_stop(yaml_cfg, logger): + '''Use curl to configure ping and set the ping rate''' + + # Get the required and optional parameters + params = yaml_cfg['parameters'] + mgmt_ip = params['mgmt_ip'] + mgmt_port = 18889 + if 'mgmt_port' in params: + mgmt_port = params['mgmt_port'] + start = 'true' + if 'start' in params: + if not params['start']: + start = 'false' + + cmd = 'curl -D /dev/stdout -H "Accept: application/vnd.yang.data' \ + '+xml" -H "Content-Type: application/vnd.yang.data+json" ' \ + '-X POST -d "{{\\"enable\\":{start}}}" ' \ + 'http://{mgmt_ip}:{mgmt_port}/api/v1/pong/adminstatus/state'. \ + format( + mgmt_ip=mgmt_ip, + mgmt_port=mgmt_port, + start=start) + + logger.debug("Executing cmd: %s", cmd) + proc = subprocess.run(cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + rc = proc.returncode + + if rc == 0: + # Check if we got 200 OK + resp = proc.stdout.decode() + if 'HTTP/1.1 200 OK' not in resp: + self._log.error("Got error response: {}".format(resp)) + rc = 1 + + return rc + + +def main(argv=sys.argv[1:]): + try: + parser = argparse.ArgumentParser() + parser.add_argument("yaml_cfg_file", type=argparse.FileType('r')) + parser.add_argument("-q", "--quiet", dest="verbose", action="store_false") + args = parser.parse_args() + + run_dir = os.path.join(os.environ['RIFT_INSTALL'], "var/run/rift") + if not os.path.exists(run_dir): + os.makedirs(run_dir) + log_file = "{}/pong_start_stop-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) + + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('pong-start-stop') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) + + ch = logging.StreamHandler() + if args.verbose: + ch.setLevel(logging.DEBUG) + else: + ch.setLevel(logging.INFO) + + # create formatter and add it to the handlers + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) + ch.setFormatter(formatter) + logger.addHandler(fh) + logger.addHandler(ch) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + + try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) + yaml_str = args.yaml_cfg_file.read() + yaml_cfg = yaml.load(yaml_str) + logger.debug("Input YAML: {}".format(yaml_cfg)) + + rc = pong_start_stop(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) + + except Exception as e: + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/examples/ping_pong_ns/rift/mano/examples/start_traffic.py b/examples/ping_pong_ns/rift/mano/examples/start_traffic.py index af6f62f6..3093792f 100755 --- a/examples/ping_pong_ns/rift/mano/examples/start_traffic.py +++ b/examples/ping_pong_ns/rift/mano/examples/start_traffic.py @@ -41,7 +41,13 @@ def start_traffic(yaml_cfg, logger): vnf_type=vnf_type) logger.debug("Executing cmd: %s", curl_cmd) - subprocess.check_call(curl_cmd, shell=True) + proc = subprocess.run(curl_cmd, shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + logger.debug("Process: {}".format(proc)) + + return proc.returncode # Enable pong service first for index, vnfr in yaml_cfg['vnfr'].items(): @@ -51,11 +57,15 @@ def start_traffic(yaml_cfg, logger): if 'pong_vnfd' in vnfr['name']: vnf_type = 'pong' port = 18889 - enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + rc = enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + if rc != 0: + logger.error("Enable service for pong failed: {}". + format(rc)) + return rc break # Add a delay to provide pong port to come up - time.sleep(0.1) + time.sleep(1) # Enable ping service next for index, vnfr in yaml_cfg['vnfr'].items(): @@ -65,9 +75,12 @@ def start_traffic(yaml_cfg, logger): if 'ping_vnfd' in vnfr['name']: vnf_type = 'ping' port = 18888 - enable_service(vnfr['mgmt_ip_address'], port, vnf_type) + rc = enable_service(vnfr['mgmt_ip_address'], port, vnf_type) break + return rc + + def main(argv=sys.argv[1:]): try: parser = argparse.ArgumentParser() @@ -79,14 +92,14 @@ def main(argv=sys.argv[1:]): if not os.path.exists(run_dir): os.makedirs(run_dir) log_file = "{}/ping_pong_start_traffic-{}.log".format(run_dir, time.strftime("%Y%m%d%H%M%S")) - logging.basicConfig(filename=log_file, level=logging.DEBUG) - logger = logging.getLogger() - except Exception as e: - print("Exception in {}: {}".format(__file__, e)) - sys.exit(1) + # logging.basicConfig(filename=log_file, level=logging.DEBUG) + logger = logging.getLogger('ping-pong-start-traffic') + logger.setLevel(logging.DEBUG) + + fh = logging.FileHandler(log_file) + fh.setLevel(logging.DEBUG) - try: ch = logging.StreamHandler() if args.verbose: ch.setLevel(logging.DEBUG) @@ -95,24 +108,28 @@ def main(argv=sys.argv[1:]): # create formatter and add it to the handlers formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') + fh.setFormatter(formatter) ch.setFormatter(formatter) + logger.addHandler(fh) logger.addHandler(ch) except Exception as e: - logger.exception(e) - raise e + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) try: + logger.debug("Input file: {}".format(args.yaml_cfg_file.name)) yaml_str = args.yaml_cfg_file.read() - # logger.debug("Input YAML file:\n{}".format(yaml_str)) yaml_cfg = yaml.load(yaml_str) logger.debug("Input YAML: {}".format(yaml_cfg)) - start_traffic(yaml_cfg, logger) + rc = start_traffic(yaml_cfg, logger) + logger.info("Return code: {}".format(rc)) + sys.exit(rc) except Exception as e: - logger.exception(e) - raise e + logger.exception("Exception in {}: {}".format(__file__, e)) + sys.exit(1) if __name__ == "__main__": main() diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py index 0400a54a..1d70b23a 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py @@ -16,10 +16,11 @@ import asyncio import concurrent.futures +import os import re +import shlex import tempfile import yaml -import os from gi.repository import ( RwDts as rwdts, @@ -94,7 +95,7 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): def riftca_log(self, name, level, log_str, *args): getattr(self._log, level)('RiftCA:({}) {}'.format(name, log_str), *args) - + @asyncio.coroutine def notify_create_vnfr(self, agent_nsr, agent_vnfr): """ @@ -140,12 +141,122 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): pass @asyncio.coroutine - def vnf_config_primitive(self, agent_nsr, agent_vnfr, primitive, output): + def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive, + vnf_config=None): + ''' + Pass vnf_config to avoid querying DTS each time + ''' + self._log.debug("VNF config primitive {} for nsr {}, vnfr {}". + format(primitive.name, nsr_id, vnfr_id)) + + if vnf_config is None: + vnfr = yield from self.get_vnfr(vnfr_id) + if vnfr is None: + self._log.error("Unable to get VNFR {} through DTS". + format(vnfr_id)) + return 1, "Unable to get VNFR {} through DTS".format(vnfr_id) + + vnf_config = vnfr.vnf_configuration + self._log.debug("VNF config= %s", vnf_config.as_dict()) + + data = {} + script = None + found = False + + configs = vnf_config.config_primitive + for config in configs: + if config.name == primitive.name: + found = True + self._log.debug("RiftCA: Found the config primitive %s", + config.name) + + spt = config.user_defined_script + if spt is None: + self._log.error("RiftCA: VNFR {}, Did not find " + "script defined in config {}". + format(vnfr['name'], config.as_dict())) + return 1, "Did not find user defined script for " \ + "config primitive {}".format(primitive.name) + + spt = shlex.quote(spt.strip()) + if spt[0] == '/': + script = spt + else: + script = os.path.join(self._rift_artif_dir, + 'launchpad/libs', + nsr_id, + 'scripts', + spt) + self._log.debug("Rift config agent: Checking for script " + "in %s", script) + if not os.path.exists(script): + self._log.debug("Rift config agent: Did not find " + "script %s", script) + script = os.path.join(self._rift_install_dir, + 'usr/bin', spt) + if not os.path.exists(script): + self._log.debug("Rift config agent: Did not find " + "script %s", script) + return 1, "Did not find user defined " \ + "script {}".format(spt) + + params = {} + for param in config.parameter: + val = None + for p in primitive.parameter: + if p.name == param.name: + val = p.value + break + + if val is None: + val = param.default_value + + if val: + val = self.convert_value(val, param.data_type) + + params.update({param.name: val}) + + data['parameters'] = params + break + + if not found: + msg = "Did not find the primitive {} in VNFR {}". \ + format(primitive.name, vnfr.name) + self._log.error(msg) + return 1, msg + + rc, script_err = yield from self.exec_script(script, data) + return rc, script_err + + @asyncio.coroutine + def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output): ''' primitives support by RiftCA + + Pass vnf_config to avoid querying DTS each time ''' - pass - + output.execution_status = "failed" + output.execution_id = '' + output.execution_error_details = '' + + try: + vnfr = self._rift_vnfs[vnfr_id].vnfr + except KeyError: + msg = "Did not find VNFR %s in RiftCA plugin", vnfr_id + self._log.error(msg) + output.execution_error_details = msg + return + + rc, err = yield from self._vnf_config_primitive(nsr_id, + vnfr_id, + primitive) + if rc == 0: + output.execution_status = "completed" + else: + if isinstance(err, bytes): + err = err.decode() + output.execution_error_details = err + @asyncio.coroutine def apply_config(self, config, nsr, vnfr, rpc_ip): """ Notification on configuration of an NSR """ @@ -265,6 +376,67 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): return task, err + @asyncio.coroutine + def apply_initial_config_new(self, agent_nsr, agent_vnfr): + self._log.debug("RiftCA: VNF initial config primitive for nsr {}, vnfr {}". + format(agent_nsr.name, agent_vnfr.name)) + + try: + vnfr = self._rift_vnfs[agent_vnfr.id].vnfr + except KeyError: + self._log.error("RiftCA: Did not find VNFR %s in RiftCA plugin", + agent_vnfr.name) + return False + + class Primitive: + def __init__(self, name): + self.name = name + self.value = None + self.parameter = [] + + vnfr = yield from self.get_vnfr(agent_vnfr.id) + if vnfr is None: + msg = "Unable to get VNFR {} ({}) through DTS". \ + format(agent_vnfr.id, agent_vnfr.name) + self._log.error(msg) + raise RuntimeError(msg) + + vnf_config = vnfr.vnf_configuration + self._log.debug("VNFR %s config: %s", vnfr.name, + vnf_config.as_dict()) + + # Sort the primitive based on the sequence number + primitives = sorted(vnf_config.initial_config_primitive, + key=lambda k: k.seq) + if not primitives: + self._log.debug("VNFR {}: No initial-config-primitive specified". + format(vnfr.name)) + return True + + for primitive in primitives: + if primitive.config_primitive_ref: + # Reference to a primitive in config primitive + prim = Primitive(primitive.config_primitive_ref) + rc, err = yield from self._vnf_config_primitive(agent_nsr.id, + agent_vnfr.id, + prim, + vnf_config) + if rc != 0: + msg = "Error executing initial config primitive" \ + " {} in VNFR {}: rc={}, stderr={}". \ + format(prim.name, vnfr.name, rc, err) + self._log.error(msg) + return False + + elif primitive.name: + msg = "Primitive {} definition in initial config " \ + "primitive for VNFR {} not supported yet". \ + format(primtive.name, vnfr.name) + self._log.error(msg) + raise NotImplementedError(msg) + + return True + @asyncio.coroutine def apply_initial_config(self, agent_nsr, agent_vnfr): """ @@ -280,16 +452,23 @@ class RiftCAConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase): agent_vnfr.set_to_configurable() if agent_vnfr.is_configurable: # apply initial config for the vnfr + # Keep the config-template based initial-config rc = yield from self._events.apply_vnf_config(agent_vnfr.vnf_cfg) + + # Check if the new method of initial-config-primitive is present + if rc: + rc = yield from self.apply_initial_config_new(agent_nsr, agent_vnfr) + else: self._log.info("Rift config agent: VNF:%s/%s is not configurable yet!", agent_nsr.name, agent_vnfr.name) + except Exception as e: self._log.error("Rift config agent: Error on initial configuration to VNF:{}/{}, e {}" .format(agent_nsr.name, agent_vnfr.name, str(e))) - + self._log.exception(e) - return rc + return False return rc diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py index b8de0d30..9312020c 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py @@ -16,10 +16,41 @@ import asyncio import abc +import os +import tempfile +import yaml + +import gi +gi.require_version('RwDts', '1.0') +from gi.repository import ( + RwDts as rwdts, +) # Default config agent plugin type DEFAULT_CAP_TYPE = "riftca" + +class XPaths(object): + @staticmethod + def nsr_opdata(k=None): + return ("D,/nsr:ns-instance-opdata/nsr:nsr" + + ("[nsr:ns-instance-config-ref='{}']".format(k) if k is not None else "")) + + @staticmethod + def nsd_msg(k=None): + return ("C,/nsd:nsd-catalog/nsd:nsd" + + "[nsd:id = '{}']".format(k) if k is not None else "") + + @staticmethod + def vnfr_opdata(k=None): + return ("D,/vnfr:vnfr-catalog/vnfr:vnfr" + + ("[vnfr:id='{}']".format(k) if k is not None else "")) + + @staticmethod + def nsr_config(k=None): + return ("C,/nsr:ns-instance-config/nsr:nsr[nsr:id='{}']".format(k) if k is not None else "") + + class RiftCMnsr(object): ''' Agent class for NSR @@ -89,6 +120,12 @@ class RiftCMnsr(object): def vnfr_ids(self): return self._vnfr_ids + def get_member_vnfr(self, member_index): + for vnfr in self._vnfrs: + if vnfr.member_vnf_index == member_index: + return vnfr + + class RiftCMvnfr(object): ''' Agent base class for VNFR processing @@ -284,6 +321,118 @@ class RiftCMConfigPluginBase(object): """Get the status of the service""" return None + # Helper functions + + def convert_value(self, value, type_='STRING'): + if type_ == 'STRING': + return str(value) + + if type_ == 'INTEGER': + return int(value) + + if type_ == 'BOOLEAN': + return (value == 1) or (value.lower() == 'true') + + return value + + @asyncio.coroutine + def _read_dts(self, xpath, do_trace=False): + self._log.debug("_read_dts path = %s", xpath) + flags = rwdts.XactFlag.MERGE + res_iter = yield from self._dts.query_read( + xpath, flags=flags + ) + + results = [] + try: + for i in res_iter: + result = yield from i + if result is not None: + results.append(result.result) + except: + pass + + return results + + + @asyncio.coroutine + def get_xpath(self, xpath): + self._log.debug("Attempting to get xpath: {}".format(xpath)) + resp = yield from self._read_dts(xpath, False) + if len(resp) > 0: + self._log.debug("Got DTS resp: {}".format(resp[0])) + return resp[0] + return None + + @asyncio.coroutine + def get_nsr(self, id): + self._log.debug("Attempting to get NSR: %s", id) + nsrl = yield from self._read_dts(XPaths.nsr_opdata(id), False) + nsr = None + if len(nsrl) > 0: + nsr = nsrl[0].as_dict() + return nsr + + @asyncio.coroutine + def get_nsr_config(self, id): + self._log.debug("Attempting to get config NSR: %s", id) + nsrl = yield from self._read_dts(XPaths.nsr_config(id), False) + nsr = None + if len(nsrl) > 0: + nsr = nsrl[0] + return nsr + + @asyncio.coroutine + def get_vnfr(self, id): + self._log.debug("Attempting to get VNFR: %s", id) + vnfrl = yield from self._read_dts(XPaths.vnfr_opdata(id), do_trace=False) + vnfr_msg = None + if len(vnfrl) > 0: + vnfr_msg = vnfrl[0] + return vnfr_msg + + @asyncio.coroutine + def exec_script(self, script, data): + """Execute a shell script with the data as yaml input file""" + self._log.debug("Execute script {} with data {}". + format(script, data)) + tmp_file = None + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + tmp_file.write(yaml.dump(data, default_flow_style=True) + .encode("UTF-8")) + + cmd = "{} {}".format(script, tmp_file.name) + self._log.debug("Running the CMD: {}".format(cmd)) + + try: + proc = yield from asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + rc = yield from proc.wait() + script_out, script_err = yield from proc.communicate() + + except Exception as e: + msg = "Script {} caused exception: {}". \ + format(script, e) + self._log.exception(msg) + rc = 1 + script_err = msg + script_out = '' + + finally: + # Remove the tempfile created + try: + os.remove(tmp_file.name) + except OSError as e: + self._log.info("Error removing tempfile {}: {}". + format(tmp_file.name, e)) + + self._log.debug("Script {}: rc={}\nStdOut:{}\nStdErr:{}". + format(script, rc, script_out, script_err)) + + return rc, script_err + @asyncio.coroutine def invoke(self, method, *args): try: diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py index f71b3f1c..26c595dd 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py @@ -241,7 +241,7 @@ class ConfigManagerConfig(object): # Initialize all handles that needs to be registered for reg in self.reg_handles: yield from reg.register() - + @asyncio.coroutine def register_cm_state_opdata(self): @@ -422,8 +422,11 @@ class ConfigManagerConfig(object): if 'config_template' in vnf_config: - vnf_cfg['cfg_template'] = '{}_{}_template.cfg'.format(nsr_obj.cfg_path_prefix, config_priority['configuration_type']) - vnf_cfg['cfg_file'] = '{}.{}'.format(nsr_obj.cfg_path_prefix, get_cfg_file_extension(method, configuration_options)) + vnf_cfg['cfg_template'] = '{}_{}_template.cfg'.format(nsr_obj.cfg_path_prefix, + config_priority['configuration_type']) + vnf_cfg['cfg_file'] = '{}.{}'.format(nsr_obj.cfg_path_prefix, + get_cfg_file_extension(method, + configuration_options)) vnf_cfg['xlate_script'] = os.path.join(self._parent.cfg_dir, 'xlate_cfg.py') try: # Now write this template into file @@ -455,103 +458,294 @@ class ConfigManagerConfig(object): nsr_obj.populate_vm_state_from_vnf_cfg() @asyncio.coroutine - def resolve_xpath(self, xpath, name, vnfd): - # For now, use DTS to resolve the path - # TODO (pjoseph): Add better xpath support + def update_config_primitives(self, nsr_obj): + + # Process all config-primitives in the member VNFs + for vnfr in nsr_obj.vnfrs: + vnfd = vnfr['vnf_cfg']['agent_vnfr'].vnfd + + try: + prims = vnfd.vnf_configuration.config_primitive + if not prims: + self._log.debug("VNFR {} with VNFD {} has no config primitives defined". + format(vnfr.name, vnfd.name)) + return + except AttributeError as e: + self._log.error("No config primitives found on VNFR {} ({})". + format(vnfr.name, vnfd.name)) + cm_state = nsr_obj.find_vnfr_cm_state(vnfr['id']) + caps = cm_state['vnf_access_point']['capability'] + deps = cm_state['vnf_access_point']['dependency'] + + vnf_configuration = vnfd.vnf_configuration.as_dict() + vnf_configuration['config_primitive'] = [] + for prim in prims: + confp = prim.as_dict() + for param in confp['parameter']: + # First check the param in capabilities + found = False + for cap in caps: + if cap['name'] == param['name']: + param['default_value'] = cap['value'] + param['read_only'] = True + break + if not found: + for dep in deps: + if dep['name'] == param['name']: + param['default_value'] = dep['value'] + param['read_only'] = True + found = True + break + self._log.debug("Config primitive: {}".format(confp)) + vnf_configuration['config_primitive'].append(confp) + + cm_state['vnf_configuration'] = vnf_configuration + + # Publish the updated config primitives for the VNFRs + yield from nsr_obj.publish_cm_state() - xpath_prefix = "C,/vnfd:vnfd-catalog/vnfd[id='{}']/vnfap/capability/capability[name='{}']/xpath". \ - format(vnfd.id, name) + @asyncio.coroutine + def get_resolved_xpath(self, xpath, name, vnf_name, xpath_prefix): + # For now, use DTS to resolve the path + # TODO (pjoseph): Add better xpath support + dts_path = xpath if xpath.startswith('../'): prefix = xpath_prefix xp = xpath while xp.startswith('../'): idx = prefix.rfind('/') if idx == -1: - raise ValueError("VNFD {}, Did not find the xpath specified: {}". - format(vnfd.name, xpath)) + raise ValueError("VNF {}, Did not find the xpath specified: {}". + format(vnf_name, xpath)) prefix = prefix[:idx] xp = xp[3:] - idx = xp.rfind('/') - if idx = -1: - raise ValueError("VNFD {}, xpath {} should point to an attribute". - format(vnfd.name, xpath)) - attr = xp[idx+1:] - dts_path = prefix + '/' + xp[:idx] - self._log.error("DTS path: {}, attribute: {}".format(dts_path, attribute)) + dts_path = prefix + '/' + xp elif xpath.startswith('/'): dts_path = 'C,' + xpath elif xpath.startswith('C,/') or xpath.startswith('D,/'): dts_path = xpath else: - self._log.error("Unknown xpath {} for capability {} in VNFD {}". - format(xpath, name, vnfd.name)) - return None + self._log.error("Invalid xpath {} for capability {} in VNF {}". + format(xpath, name, vnf_name)) + raise ValueError("XPath {} in capability {} for VNF {} is invalid". + format(xpath, name, vnf_name)) + return dts_path + + @asyncio.coroutine + def resolve_xpath(self, xpath, name, vnfd): + xpath_prefix = "C,/vnfd:vnfd-catalog/vnfd[id='{}']/vnfap/capability" \ + "/capability[name='{}']/xpath". \ + format(vnfd.id, name) + + dts_path = yield from self.get_resolved_xpath(xpath, name, + vnfd.name, xpath_prefix) + idx = dts_path.rfind('/') + if idx == -1: + raise ValueError("VNFD {}, xpath {} should point to an attribute". + format(vnfd.name, xpath)) + attr = dts_path[idx+1:] + dts_path = dts_path[:idx] + self._log.debug("DTS path: {}, attribute: {}".format(dts_path, attr)) resp = yield from self.cmdts_obj.get_xpath(dts_path) - self._log.error("DTS response: {}".resp) + if resp is None: + raise ValueError("Xpath {} in capability {} for VNFD {} is not found". + format(xpath, name, vnfd.name)) + self._log.debug("DTS response: {}".format(resp.as_dict())) + + try: + val = getattr(resp, attr) + except AttributeError as e: + self._log.error("Did not find attribute : {}".format(attr)) + try: + val = getattr(resp, attr.replace('-', '_')) + except AttributeError as e: + raise ValueError("Did not find attribute {} in XPath {} " + "for capability {} in VNF {}". + format(attr, dts_path, vnfd.name)) + + self._log.debug("XPath {}: {}".format(xpath, val)) + return val @asyncio.coroutine - def process_vnf_access_point(self, nsr_obj): - nsd = nsr_obj.agent_nsr.nsd - vnfap_map = None + def resolve_attribute(self, attribute, name, vnfd, vnfr): + idx = attribute.rfind(',') + if idx == -1: + raise ValueError ("Invalid attribute {} for capability {} in " + "VNFD specified". + format(attribute, name, vnfd.name)) + xpath = attribute[:idx].strip() + attr = attribute[idx+1:].strip() + self._log.debug("Attribute {}, {}".format(xpath, attr)) + if xpath.startswith('C,/'): + raise ValueError("Attribute {} for capability {} in VNFD cannot " + "be a config". + format(attribute, name, vnfd.name)) + + xpath_prefix = "D,/vnfr:vnfr-catalog/vnfr[id='{}']/vnfap/capability" \ + "/capability[name='{}']/attribute". \ + format(vnfr['id'], name) + dts_path = yield from self.get_resolved_xpath(xpath, name, + vnfr['name'], + xpath_prefix) + self._log.debug("DTS query: {}".format(dts_path)) + + resp = yield from self.cmdts_obj.get_xpath(dts_path) + if resp is None: + raise ValueError("Attribute {} in capability {} for VNFD {} is " \ + "not found". + format(xpath, name, vnfd.name)) + self._log.debug("DTS response: {}".format(resp.as_dict())) + try: - vnfap_map = nsd.vnfap_map + val = getattr(resp, attr) except AttributeError as e: - self._log.error("No vnfap map specified for nsr: {}". - format(nsr_obj.nsr_name)) - return + self._log.debug("Did not find attribute {}".format(attr)) + try: + val = getattr(resp, attr.replace('-', '_')) + except AttributeError as e: + raise ValueError("Did not find attribute {} in XPath {} " + "for capability {} in VNF {}". + format(attr, dts_path, vnfd.name)) - if not len(vnfap_map): - self._log.error("No entries in vnfap map for nsr: {}". - format(nsr_obj.nsr_name)) - return + self._log.debug("Attribute {}: {}".format(attribute, val)) + return val + + @asyncio.coroutine + def process_vnf_access_point(self, nsr_obj): + nsd = nsr_obj.agent_nsr.nsd # Process all capabilities in all the member VNFs for vnfr in nsr_obj.vnfrs: vnfd = vnfr['vnf_cfg']['agent_vnfr'].vnfd - self._log.error("VNFD: {}".format(vnfd.as_dict())) + try: vnfap = vnfd.vnfap except AttributeError as e: - self._log.error("VNFR {} does not have VNF access point". + self._log.debug("VNFR {} does not have VNF access point". format(vnfr.name)) continue + caps = [] try: caps = vnfap.capability except AttributeError as e: - self._log.error("VNFR {} has no capabilities defined". + self._log.debug("VNFR {} has no capabilities defined". format(vnfr.name)) - continue # Get the cm state dict for this vnfr cm_state = nsr_obj.find_vnfr_cm_state(vnfr['id']) - cm_caps = cm_state['vnf_access_point']['capability'] + cm_caps = [] for cap in caps: - self._log.error("VNFR {}: capability {}". + self._log.debug("VNFR {}: capability {}". format(vnfr['name'], cap.as_dict())) - val = None try: val = cap.value - self._log.error("Got value {}".format(val)) + self._log.debug("Got value {}".format(val)) + if val: + cm_caps.append({'name': cap.name, 'value': str(val)}) + continue except AttributeError as e: pass - if val is None: - try: - xpath = cap.xpath - # resolve xpath + try: + xpath = cap.xpath + # resolve xpath + if xpath: val = yield from self.resolve_xpath(xpath, cap.name, vnfd) self._log.debug("Got xpath value: {}".format(val)) - except AttributeError as e: - pass + cm_caps.append({'name': cap.name, 'value': str(val)}) + continue + except AttributeError as e: + pass + + try: + attribute = cap.attribute + # resolve attribute + if attribute: + val = yield from self.resolve_attribute(attribute, cap.name, vnfd, vnfr) + self._log.debug("Got attribute value: {}".format(val)) + cm_caps.append({'name': cap.name, 'value': str(val)}) + continue + except AttributeError as e: + pass + + self._log.debug("VNF Capabilities: {}".format(cm_caps)) + cm_state['vnf_access_point']['capability'] = cm_caps + + try: + deps = vnfap.dependency + except AttributeError as e: + self._log.debug("VNFR {} has no dependencies defined". + format(vnfr.name)) + continue + + cm_deps = [] + for dep in deps: + self._log.debug("VNFR{}: dependency {}". + format(vnfr['name'], dep.as_dict())) + cm_deps.append({'name': dep.name, 'mandatory': dep.mandatory}) + + self._log.debug("VNF Dependencies: {}".format(cm_deps)) + cm_state['vnf_access_point']['dependency'] = cm_deps + + # Publish all capabilities for the VNFRs + yield from nsr_obj.publish_cm_state() + + vnfap_map = None + try: + vnfap_map = nsd.vnfap_map + except AttributeError as e: + self._log.debug("No vnfap map specified for nsr: {}". + format(nsr_obj.nsr_name)) + return + if not len(vnfap_map): + self._log.info("No entries in vnfap map for nsr: {}". + format(nsr_obj.nsr_name)) + return + + for ap in vnfap_map: + cap_vnfr = nsr_obj.agent_nsr.get_member_vnfr(ap.capability.member_vnf_index) + cm_state = nsr_obj.find_vnfr_cm_state(cap_vnfr.id) + if cm_state is None: + raise ValueError("Capabilities are not defined for VNF member {} ({})". + format(ap.capability.member_vnf_index, cap_vnfr.name)) + caps = cm_state['vnf_access_point']['capability'] + + cap_attr = ap.capability.capability_ref + val = None + for cap in caps: + if cap['name'] == cap_attr: + val = cap['value'] + break + + dep_vnfr = nsr_obj.agent_nsr.get_member_vnfr(ap.dependency.member_vnf_index) + dep_attr = ap.dependency.dependency_ref + cm_state = nsr_obj.find_vnfr_cm_state(dep_vnfr.id) + try: + cm_deps = cm_state['vnf_access_point']['dependency'] + except KeyError as e: + raise ValueError("VNFR index {} ({}) has no dependencies defined". + format(ap.dependency.member_vnf_index, dep_vnfr['name'])) + + for i, item in enumerate(cm_deps): + if item['name'] == dep_attr: + item['value'] = str(val) + cm_deps[i] = item + self._log.debug("Dependency in VNFR {}: {}". + format(dep_vnfr.name, item)) + break + + # Publish resolved dependencies for the VNFRs + yield from nsr_obj.publish_cm_state() + yield from self.update_config_primitives(nsr_obj) @asyncio.coroutine def config_NSR(self, id): @@ -704,7 +898,8 @@ class ConfigManagerConfig(object): # Find vnfr for this vnf_unique_name if vnf_unique_name not in nsr_obj._vnfr_dict: - self._log.error("NS (%s) - Can not find VNF to be configured: %s", nsr_obj.nsr_name, vnf_unique_name) + self._log.error("NS (%s) - Can not find VNF to be configured: %s", + nsr_obj.nsr_name, vnf_unique_name) else: # Save this unique VNF's config input parameters nsr_obj.vnf_config_attributes_dict[vnf_unique_name] = vnf_config_attributes_dict @@ -911,12 +1106,18 @@ class ConfigManagerConfig(object): self._log.debug("Running the CMD: {}".format(cmd)) process = yield from asyncio. \ - create_subprocess_shell(cmd, loop=self._loop) + create_subprocess_shell( + cmd, loop=self._loop, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) yield from process.wait() if process.returncode: + script_out, script_err = yield from proc.communicate() msg = "NSR {} initial config using {} failed with {}". \ format(nsr_name, script, process.returncode) self._log.error(msg) + self._log.error("Script {} stderr: {}". + format(script, script_err)) raise InitialConfigError(msg) else: os.remove(inp_file) @@ -1073,30 +1274,30 @@ class ConfigManagerNSR(object): self._cp_dict['rw_mgmt_ip'] = vnf_cfg['mgmt_ip_address'] self._cp_dict['rw_username'] = vnf_cfg['username'] self._cp_dict['rw_password'] = vnf_cfg['password'] - ############################################################ - # TBD - Need to lookup above 3 for a given VNF, not global # - # Once we do that no need to dump below file again before # - # each VNF configuration translation. # - # This will require all existing config templates to be # - # changed for above three tags to include member index # - ############################################################ - try: - nsr_obj = vnf_cfg['nsr_obj'] - # Generate config_config_attributes.yaml (For debug reference) - with open(nsr_obj.xlate_dict_file, "w") as yf: - yf.write(yaml.dump(nsr_obj._cp_dict, default_flow_style=False)) - except Exception as e: - self._log.error("NS:(%s) failed to write nsr xlate tags file as (%s)", nsr_obj.nsr_name, str(e)) if 'cfg_template' in vnf_cfg: - script_cmd = 'python3 {} -i {} -o {} -x "{}"'.format(vnf_cfg['xlate_script'], vnf_cfg['cfg_template'], vnf_cfg['cfg_file'], self.xlate_dict_file) - self._log.debug("xlate script command (%s)", script_cmd) - #xlate_msg = subprocess.check_output(script_cmd).decode('utf-8') - xlate_msg = subprocess.check_output(script_cmd, shell=True).decode('utf-8') - self._log.info("xlate script output (%s)", xlate_msg) + try: + nsr_obj = vnf_cfg['nsr_obj'] + # Generate config_config_attributes.yaml (For debug reference) + with open(nsr_obj.xlate_dict_file, "w") as yf: + yf.write(yaml.dump(nsr_obj._cp_dict, default_flow_style=False)) + except Exception as e: + self._log.error("NS:(%s) failed to write nsr xlate tags " + "file as (%s)", nsr_obj.nsr_name, str(e)) + + script_cmd = 'python3 {} -i {} -o {} -x "{}"'. \ + format(vnf_cfg['xlate_script'], + vnf_cfg['cfg_template'], + vnf_cfg['cfg_file'], + self.xlate_dict_file) + self._log.debug("xlate script command (%s)", script_cmd) + xlate_msg = subprocess.check_output(script_cmd, shell=True).decode('utf-8') + self._log.info("xlate script output (%s)", xlate_msg) + except Exception as e: vnf_cm_state['state'] = self.state_to_string(conmanY.RecordState.CFG_PROCESS_FAILED) - self._log.error("Failed to execute translation script for VNF: %s with (%s)", log_this_vnf(vnf_cfg), str(e)) + self._log.error("Failed to execute translation script for VNF: %s with (%s)", + log_this_vnf(vnf_cfg), str(e)) return self._log.info("Applying config to VNF: %s = %s!", log_this_vnf(vnf_cfg), vnf_cfg) @@ -1456,9 +1657,12 @@ class ConfigManagerDTS(object): @asyncio.coroutine def get_xpath(self, xpath): - self._log.debug("Attempting to get {}".format(xpath)) + self._log.debug("Attempting to get xpath: {}".format(xpath)) resp = yield from self._read_dts(xpath, False) - return resp + if len(resp) > 0: + self._log.debug("Got DTS resp: {}".format(resp[0])) + return resp[0] + return None @asyncio.coroutine def get_nsr(self, id): @@ -1549,7 +1753,7 @@ class ConfigManagerDTS(object): def register(self): yield from self.register_to_publish() yield from self.register_for_nsr() - + @asyncio.coroutine def register_to_publish(self): ''' Register to DTS for publishing cm-state opdata ''' diff --git a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py index f292a689..0b2e5763 100644 --- a/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py +++ b/rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py @@ -55,9 +55,12 @@ def log_this_vnf(vnf_cfg): else: log_vnf += "{}/".format(vnf_cfg[item]) return log_vnf - + + class ConfigManagerROifConnectionError(Exception): pass + + class ScriptError(Exception): pass @@ -78,23 +81,23 @@ class ConfigManagerEvents(object): def update_vnf_state(self, vnf_cfg, state): nsr_obj = vnf_cfg['nsr_obj'] yield from nsr_obj.update_vnf_cm_state(vnf_cfg['vnfr'], state) - + @asyncio.coroutine def apply_vnf_config(self, vnf_cfg): self._log.debug("apply_vnf_config VNF:{}" .format(log_this_vnf(vnf_cfg))) - + if vnf_cfg['config_delay']: yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_DELAY) yield from asyncio.sleep(vnf_cfg['config_delay'], loop=self._loop) - + # See if we are still alive! if vnf_cfg['nsr_obj'].being_deleted: # Don't do anything, just return self._log.info("VNF : %s is being deleted, skipping configuration!", log_this_vnf(vnf_cfg)) return True - + yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_SEND) try: if vnf_cfg['config_method'] == 'netconf': @@ -124,21 +127,20 @@ class ConfigManagerEvents(object): yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.CFG_FAILED) return True - #Update VNF state - yield from self.update_vnf_state(vnf_cfg, conmanY.RecordState.READY) - self._log.info("Successfully applied configuration to VNF: %s", + self._log.info("Successfully applied config template to VNF: %s", log_this_vnf(vnf_cfg)) + except Exception as e: self._log.error("Applying configuration(%s) file(%s) to VNF: %s failed as: %s", vnf_cfg['config_method'], vnf_cfg['cfg_file'], log_this_vnf(vnf_cfg), str(e)) - #raise + self._log.exception(e) return False return True - + class ConfigManagerVNFscriptconf(object): def __init__(self, log, loop, parent, vnf_cfg): @@ -151,7 +153,12 @@ class ConfigManagerVNFscriptconf(object): #@asyncio.coroutine def apply_edit_cfg(self): vnf_cfg = self._vnf_cfg - self._log.debug("Attempting to apply scriptconf to VNF: %s", log_this_vnf(vnf_cfg)) + self._log.debug("Attempting to apply scriptconf to VNF %s: %s", log_this_vnf(vnf_cfg), vnf_cfg) + + if vnf_cfg['cfg_file'] is None or (vnf_cfg['cfg_file'] == 'None'): + self._log.debug("Config file for script not provided") + return + try: st = os.stat(vnf_cfg['cfg_file']) os.chmod(vnf_cfg['cfg_file'], st.st_mode | stat.S_IEXEC) @@ -297,6 +304,10 @@ class ConfigManagerVNFnetconf(object): vnf_cfg = self._vnf_cfg self._log.debug("Attempting to apply netconf to VNF: %s", log_this_vnf(vnf_cfg)) + if vnf_cfg['cfg_file'] is None or (vnf_cfg['cfg_file'] == 'None'): + self._log.debug("Config file for Netconf not provided") + return + if self._manager is None: self._log.error("Netconf is not connected to VNF: %s, aborting!", log_this_vnf(vnf_cfg)) return diff --git a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py index 4ab351e7..5beafe60 100644 --- a/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py +++ b/rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py @@ -628,9 +628,15 @@ class MonitorTasklet(rift.tasklets.Tasklet): self.dts.handle.set_state(next_state) def on_vnfr_create(self, vnfr): - if not self.monitor.nfvi_metrics_available(vnfr.cloud_account): + try: + acc = vnfr.cloud_account + except AttributeError as e: + self.log.warning("NFVI metrics not supported") + return + + if not self.monitor.nfvi_metrics_available(acc): msg = "NFVI metrics unavailable for {}" - self.log.warning(msg.format(vnfr.cloud_account)) + self.log.warning(msg.format(acc)) return self.monitor.add_vnfr(vnfr) @@ -642,6 +648,12 @@ class MonitorTasklet(rift.tasklets.Tasklet): self.loop.create_task(coro) def on_vnfr_update(self, vnfr): + try: + acc = vnfr.cloud_account + except AttributeError as e: + self.log.warning("NFVI metrics not supported") + return + if not self.monitor.nfvi_metrics_available(vnfr.cloud_account): msg = "NFVI metrics unavailable for {}" self.log.warning(msg.format(vnfr.cloud_account)) diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py index 23ab7b6e..8073c4cf 100644 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py @@ -83,33 +83,38 @@ class ROConfigManager(object): return try: - nsrid = cm_nsr['id'] + nsrid = cm_nsr.id # Update the VNFRs' config status - gen = [] - if 'cm_vnfr' in cm_nsr: - gen = (vnfr for vnfr in cm_nsr['cm_vnfr'] - if vnfr['id'] in self.nsm._vnfrs) + gen = (vnfr for vnfr in cm_nsr.cm_vnfr + if vnfr.id in self.nsm._vnfrs) for vnfr in gen: - vnfrid = vnfr['id'] - new_status = ROConfigManager.map_config_status(vnfr['state']) + vnfrid = vnfr.id + new_status = ROConfigManager.map_config_status(vnfr.state) self._log.debug("Updating config status of VNFR {} " \ "in NSR {} to {}({})". format(vnfrid, nsrid, new_status, - vnfr['state'])) + vnfr.state)) yield from \ self.nsm.vnfrs[vnfrid].set_config_status(new_status) + yield from \ + self.nsm.vnfrs[vnfrid].update_config_primitives( + vnfr.vnf_configuration) + # Update the NSR's config status - new_status = ROConfigManager.map_config_status(cm_nsr['state']) - self._log.info("Updating config status of NSR {} to {}({})". - format(nsrid, new_status, cm_nsr['state'])) + new_status = ROConfigManager.map_config_status(cm_nsr.state) + self._log.debug("Updating config status of NSR {} to {}({})". + format(nsrid, new_status, cm_nsr.state)) - # If terminate nsr request comes when NS instantiation is in 'Configuring state'; self.nsm.nsrs dict - # is already empty when self.nsm.nsrs[nsrid].set_config_status gets executed. So adding a check here. + # If terminate nsr request comes when NS instantiation is in + # 'Configuring state'; self.nsm.nsrs dict is already empty when + # self.nsm.nsrs[nsrid].set_config_status gets executed. So adding a check here. if nsrid in self.nsm.nsrs: - yield from self.nsm.nsrs[nsrid].set_config_status(new_status, cm_nsr.get('state_details')) + yield from self.nsm.nsrs[nsrid].set_config_status( + new_status, + cm_nsr.state_details) except Exception as e: self._log.error("Failed to process cm-state for nsr {}: {}". @@ -119,12 +124,11 @@ class ROConfigManager(object): @asyncio.coroutine def register(self): """ Register for cm-state changes """ - + @asyncio.coroutine def on_prepare(xact_info, query_action, ks_path, msg): """ cm-state changed """ - #print("###>>> cm-state change ({}), msg_dict = {}".format(query_action, msg_dict)) self._log.debug("Received cm-state on_prepare (%s:%s:%s)", query_action, ks_path, @@ -133,10 +137,11 @@ class ROConfigManager(object): if (query_action == rwdts.QueryAction.UPDATE or query_action == rwdts.QueryAction.CREATE): # Update Each NSR/VNFR state - msg_dict = msg.as_dict() - yield from self.update_ns_cfg_state(msg_dict) + # msg_dict = msg.as_dict() + yield from self.update_ns_cfg_state(msg) elif query_action == rwdts.QueryAction.DELETE: - self._log.debug("DELETE action in on_prepare for cm-state, ignoring") + self._log.debug("DELETE action in on_prepare for cm-state, " + "ignoring") else: raise NotImplementedError( "%s on cm-state is not supported", @@ -145,10 +150,13 @@ class ROConfigManager(object): xact_info.respond_xpath(rwdts.XactRspCode.ACK) try: - handler = rift.tasklets.DTS.RegistrationHandler(on_prepare=on_prepare) - self.dts_reg_hdl = yield from self._dts.register(self.cm_state_xpath, - flags=rwdts.Flag.SUBSCRIBER, - handler=handler) + handler = rift.tasklets.DTS.RegistrationHandler( + on_prepare=on_prepare) + self.dts_reg_hdl = yield from self._dts.register( + self.cm_state_xpath, + flags=rwdts.Flag.SUBSCRIBER, + handler=handler) + except Exception as e: - self._log.error("Failed to register for cm-state changes as %s", str(e)) - + self._log.error("Failed to register for cm-state changes as %s", + str(e)) diff --git a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py index bbe15524..6396b953 100755 --- a/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py +++ b/rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py @@ -42,6 +42,7 @@ gi.require_version('RwNsrYang', '1.0') gi.require_version('RwTypes', '1.0') gi.require_version('RwVlrYang', '1.0') gi.require_version('RwVnfrYang', '1.0') +gi.require_version('VnfdYang', '1.0') from gi.repository import ( RwYang, RwNsrYang, @@ -54,6 +55,7 @@ from gi.repository import ( RwsdnYang, RwDts as rwdts, RwTypes, + VnfdYang, ProtobufC, ) @@ -829,7 +831,10 @@ class VirtualNetworkFunctionRecord(object): @property def const_vnfr_msg(self): """ VNFR message """ - return RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConstituentVnfrRef(vnfr_id=self.id,cloud_account=self.cloud_account_name,om_datacenter=self._om_datacenter_name) + return RwNsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ConstituentVnfrRef( + vnfr_id=self.id, + cloud_account=self.cloud_account_name, + om_datacenter=self._om_datacenter_name) @property def vnfd(self): @@ -979,7 +984,7 @@ class VirtualNetworkFunctionRecord(object): format(self.name, self.vnfr_msg)) yield from self._dts.query_update( self.xpath, - rwdts.XactFlag.TRACE, + rwdts.XactFlag.REPLACE, self.vnfr_msg ) @@ -1040,6 +1045,47 @@ class VirtualNetworkFunctionRecord(object): return False + @asyncio.coroutine + def update_config_primitives(self, vnf_config): + # Update only after we are configured + if self._config_status == NsrYang.ConfigStates.INIT: + return + + if not vnf_config.as_dict(): + return + + self._log.debug("Update VNFR {} config: {}". + format(self.name, vnf_config.as_dict())) + + # Update config primitive + updated = False + for prim in self._vnfd.vnf_configuration.config_primitive: + for p in vnf_config.config_primitive: + if prim.name == p.name: + for param in prim.parameter: + for pa in p.parameter: + if pa.name == param.name: + if not param.default_value or \ + (pa.default_value != param.default_value): + param.default_value = pa.default_value + updated = True + break + break + + if updated: + self._log.debug("Updated VNFD {} config: {}". + format(self._vnfd.name, + self._vnfd.vnf_configuration)) + self._vnfr_msg = self.create_vnfr_msg() + + try: + yield from self.update_vnfm() + except Exception as e: + self._log.error("Exception updating VNFM with new config " + "primitive for VNFR {}: {}". + format(self.name, e)) + self._log.exception(e) + @asyncio.coroutine def instantiate(self, nsr): """ Instantiate this VNFR""" diff --git a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py index 36f0351f..6f66cb58 100755 --- a/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py +++ b/rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py @@ -1278,6 +1278,17 @@ class VirtualNetworkFunctionRecord(object): return vnfr_msg + @asyncio.coroutine + def update_config(self, msg, xact): + self._log.debug("VNFM vnf config: {}". + format(msg.vnf_configuration.as_dict())) + self._config_status = msg.config_status + self._vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict( + msg.as_dict()) + self._log.debug("VNFR msg config: {}". + format(self.msg.vnf_configuration.as_dict())) + yield from self.publish(xact) + @property def dashboard_url(self): ip, cfg_port = self.mgmt_intf_info() @@ -2150,9 +2161,8 @@ class VnfrDtsHandler(object): self._log.debug("VNFR {} update config status {} (current {})". format(vnfr.name, msg.config_status, vnfr.config_status)) - # Update the config status and publish - vnfr._config_status = msg.config_status - yield from vnfr.publish(None) + # Update the config and publish + yield from vnfr.update_config(msg, xact_info) else: raise NotImplementedError( @@ -2172,7 +2182,6 @@ class VnfrDtsHandler(object): handler=hdl, flags=(rwdts.Flag.PUBLISHER | rwdts.Flag.NO_PREP_READ | - rwdts.Flag.CACHE | rwdts.Flag.DATASTORE),) @asyncio.coroutine @@ -2188,13 +2197,13 @@ class VnfrDtsHandler(object): xact, path, msg) @asyncio.coroutine - def update(self, xact, path, msg): + def update(self, xact, path, msg, flags=rwdts.XactFlag.REPLACE): """ Update a VNFR record in DTS with path and message """ self._log.debug("Updating VNFR xact = %s, %s:%s", xact, path, msg) - self.regh.update_element(path, msg) + self.regh.update_element(path, msg, flags) self._log.debug("Updated VNFR xact = %s, %s:%s", xact, path, msg)