New Feature : Addtional changes for VNF access point support 41/741/1
authorPhilip Joseph <philip.joseph@riftio.com>
Fri, 25 Nov 2016 12:36:27 +0000 (07:36 -0500)
committerPhilip Joseph <philip.joseph@riftio.com>
Mon, 28 Nov 2016 19:37:20 +0000 (01:07 +0530)
Signed-off-by: Philip Joseph <philip.joseph@riftio.com>
18 files changed:
examples/ping_pong_ns/CMakeLists.txt
examples/ping_pong_ns/rift/mano/examples/ping_config.py [deleted file]
examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py
examples/ping_pong_ns/rift/mano/examples/ping_rate.py [new file with mode: 0755]
examples/ping_pong_ns/rift/mano/examples/ping_scale.py [new file with mode: 0755]
examples/ping_pong_ns/rift/mano/examples/ping_setup.py [new file with mode: 0755]
examples/ping_pong_ns/rift/mano/examples/ping_start_stop.py [new file with mode: 0755]
examples/ping_pong_ns/rift/mano/examples/pong_setup.py [new file with mode: 0755]
examples/ping_pong_ns/rift/mano/examples/pong_start_stop.py [new file with mode: 0755]
examples/ping_pong_ns/rift/mano/examples/start_traffic.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py
rwlaunchpad/plugins/rwmonitor/rift/tasklets/rwmonitor/tasklet.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py
rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py

index e373c2a..2dd5b28 100644 (file)
@@ -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 (executable)
index 4e5fd35..0000000
+++ /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()
index 919c5b7..8265f45 100755 (executable)
@@ -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 (executable)
index 0000000..b3ab07b
--- /dev/null
@@ -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 (executable)
index 0000000..4e5fd35
--- /dev/null
@@ -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 (executable)
index 0000000..c05e669
--- /dev/null
@@ -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 (executable)
index 0000000..76f653c
--- /dev/null
@@ -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 (executable)
index 0000000..cd56eca
--- /dev/null
@@ -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 (executable)
index 0000000..b5195dd
--- /dev/null
@@ -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()
index af6f62f..3093792 100755 (executable)
@@ -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()
index 0400a54..1d70b23 100644 (file)
 
 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
 
index b8de0d3..9312020 100644 (file)
 
 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:
index f71b3f1..26c595d 100644 (file)
@@ -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 '''
index f292a68..0b2e576 100644 (file)
@@ -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
index 4ab351e..5beafe6 100644 (file)
@@ -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))
index 23ab7b6..8073c4c 100644 (file)
@@ -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))
index bbe1552..6396b95 100755 (executable)
@@ -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"""
index 36f0351..6f66cb5 100755 (executable)
@@ -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)