New Feature: VCA config parameter request and source 60/860/1
authorPhilip Joseph <philip.joseph@riftio.com>
Mon, 14 Nov 2016 06:57:38 +0000 (06:57 +0000)
committerPhilip Joseph <philip.joseph@riftio.com>
Thu, 29 Dec 2016 13:39:20 +0000 (13:39 +0000)
Feature name changed from VNF access point

Signed-off-by: Philip Joseph <philip.joseph@riftio.com>
30 files changed:
common/python/rift/mano/config_data/config.py
common/python/rift/mano/config_data/test/test_converter.py
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
models/plugins/yang/mano-types.yang
models/plugins/yang/nsd.yang
models/plugins/yang/vnfd.yang
rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCA.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/RiftCM_rpc.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/jujuconf.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/riftcm_config_plugin.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_conagent.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_config.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconman_events.py
rwcm/plugins/rwconman/rift/tasklets/rwconmantasklet/rwconmantasklet.py
rwcm/plugins/yang/rw-conman.yang
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
rwlaunchpad/test/mano_ut.py

index 63a2e48..5210904 100644 (file)
@@ -250,7 +250,7 @@ class ConfigPrimitiveConvertor(object):
     def extract_vnfd_config(self, vnfd, format="yaml"):
         config_prim = None
         try:
-            config_prim = vnfd.vnf_configuration.service_primitive
+            config_prim = vnfd.vnf_configuration.config_primitive
         except AttributeError:
             pass
 
@@ -339,7 +339,7 @@ class ConfigPrimitiveConvertor(object):
                 vnfd_init_cfg_prim_msg.append(prim)
 
     def merge_vnfd_config(self, vnfd, input_data):
-        for config_primitive in vnfd.vnf_configuration.service_primitive:
+        for config_primitive in vnfd.vnf_configuration.config_primitive:
             try:
                 cfg = input_data[self.CONFIG_PRIMITIVE][config_primitive.name]
             except KeyError:
index 1bfd7d7..4d29b67 100644 (file)
@@ -222,14 +222,14 @@ def test_vnfd_config_prim(vnfd, convertor):
             ],
         })
 
-    vnf_config.service_primitive.add().from_dict({
+    vnf_config.config_primitive.add().from_dict({
         "name": "PE1",
         "parameter": [
                 {"name": "Foo", "default_value": "Bar"}
         ]
         })
 
-    expected_yaml = """service_primitive:
+    expected_yaml = """config_primitive:
   PE1:
     parameter:
       Foo: Bar
@@ -267,12 +267,12 @@ def test_vnfd_merge(vnfd, convertor):
             "parameter": [{"name": "cidr"}],
         })
 
-    vnf_config.service_primitive.add().from_dict({
+    vnf_config.config_primitive.add().from_dict({
         "name": "PE1",
         "parameter": [{"name": "Foo",}]
         })
 
-    ip_yaml = """service_primitive:
+    ip_yaml = """config_primitive:
   PE1:
     parameter:
       Foo: Bar
@@ -311,7 +311,7 @@ initial_config_primitive:
             ],
         })
 
-    vnf_config.service_primitive.add().from_dict({
+    vnf_config.config_primitive.add().from_dict({
         "name": "PE1",
         "parameter": [
                 {"name": "Foo", "default_value": "Bar"}
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 d84a912..6688c76 100755 (executable)
@@ -125,9 +125,285 @@ class VirtualNetworkFunction(ManoDescriptor):
     def add_placement_group(self, group):
         self._placement_groups.append(group)
 
+    def add_vnf_access_point(self, mano_ut=False):
+        vnfd = self.descriptor.vnfd[0]
+        confparam = vnfd.config_parameter
+
+        def get_params(param):
+            # Check if pong
+            if 'pong_' in self.name:
+                params = [
+                    {
+                        "config_primitive_name_ref": "config",
+                        "config_primitive_parameter_ref": param
+                    },
+                    {
+                        "config_primitive_name_ref": "start-stop",
+                        "config_primitive_parameter_ref": param
+                    },
+                ]
+            else:
+                params = [
+                    {
+                        "config_primitive_name_ref": "config",
+                        "config_primitive_parameter_ref": param
+                    },
+                    {
+                        "config_primitive_name_ref": "set-rate",
+                        "config_primitive_parameter_ref": param
+                    },
+                    {
+                        "config_primitive_name_ref": "start-stop",
+                        "config_primitive_parameter_ref": param
+                    },
+                ]
+            return params
+
+        src = confparam.create_config_parameter_source()
+        src.from_dict({
+            "name": "mgmt_ip",
+            "description": "Management address",
+            "attribute": "../../../../mgmt-interface, ip-address",
+            "parameter" : get_params("mgmt_ip")
+        })
+        confparam.config_parameter_source.append(src)
+        src = confparam.create_config_parameter_source()
+        src.from_dict({
+            "name": "mgmt_port",
+            "description": "Management port",
+            "descriptor": "../../../../mgmt-interface/port",
+            "parameter" : get_params("mgmt_port")
+        })
+        confparam.config_parameter_source.append(src)
+        src = confparam.create_config_parameter_source()
+        src.from_dict({
+            "name": "username",
+            "description": "Management username",
+            "value": "admin",
+            "parameter" : get_params("username")
+        })
+        confparam.config_parameter_source.append(src)
+        src = confparam.create_config_parameter_source()
+        src.from_dict({
+            "name": "password",
+            "description": "Management password",
+            "value": "admin",
+            "parameter" : get_params("password")
+        })
+        confparam.config_parameter_source.append(src)
+
+        # Check if pong
+        if 'pong_' in self.name:
+            src = confparam.create_config_parameter_source()
+            src.from_dict({
+                "name": "service_ip",
+                "description": "IP on which Pong service is listening",
+                "attribute": "../../../../connection-point[name='pong_vnfd/cp0'], ip-address",
+                "parameter" : [
+                    {
+                        "config_primitive_name_ref": "config",
+                        "config_primitive_parameter_ref": "service_ip"
+                    },
+                ]
+            })
+            confparam.config_parameter_source.append(src)
+            src = confparam.create_config_parameter_source()
+            src.from_dict({
+                "name": "service_port",
+                "description": "Port on which server listens for incoming data packets",
+                "value": "5555",
+                "parameter" : [
+                    {
+                        "config_primitive_name_ref": "config",
+                        "config_primitive_parameter_ref": "service_port"
+                    },
+                ]
+            })
+            confparam.config_parameter_source.append(src)
+
+        else:
+            src = confparam.create_config_parameter_source()
+            src.from_dict({
+                "name": "rate",
+                "description": "Rate of packet generation",
+                "value": "5",
+                "parameter" : [
+                    {
+                        "config_primitive_name_ref": "set-rate",
+                        "config_primitive_parameter_ref": "rate"
+                    },
+                ]
+            })
+            confparam.config_parameter_source.append(src)
+
+            req = confparam.create_config_parameter_request()
+            req.from_dict({
+                "name": "pong_ip",
+                "description": "IP on which Pong service is listening",
+                "parameter" : [
+                    {
+                        "config_primitive_name_ref": "config",
+                        "config_primitive_parameter_ref": "pong_ip"
+                    },
+                ]
+            })
+            confparam.config_parameter_request.append(req)
+            req = confparam.create_config_parameter_request()
+            req.from_dict({
+                "name": "pong_port",
+                "description": "Port on which Pong service is listening",
+                "parameter" : [
+                    {
+                        "config_primitive_name_ref": "config",
+                        "config_primitive_parameter_ref": "pong_port"
+                    },
+                ]
+            })
+            confparam.config_parameter_request.append(req)
+
+    def add_ping_config(self, mano_ut=False, use_ns_init_conf=False):
+        vnfd = self.descriptor.vnfd[0]
+        # Add vnf configuration
+        vnf_config = vnfd.vnf_configuration
+
+        # vnf_config.config_attributes.config_delay = 10
+
+        # Select "script" configuration
+        vnf_config.script.script_type = 'bash'
+
+        # Add config primitive
+        vnf_config.create_config_primitive()
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "config",
+            "parameter": [
+                {"name": "mgmt_ip", "data_type": "STRING"},
+                {"name": "mgmt_port", "data_type": "INTEGER"},
+                {"name": "username", "data_type": "STRING"},
+                {"name": "password", "data_type": "STRING"},
+                {"name": "pong_ip", "data_type": "STRING"},
+                {"name": "pong_port", "data_type": "INTEGER",
+                 "default_value": "5555"},
+            ],
+            "user_defined_script": "ping_setup.py",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "set-rate",
+            "parameter": [
+                {"name": "mgmt_ip", "data_type": "STRING"},
+                {"name": "mgmt_port", "data_type": "INTEGER"},
+                {"name": "username", "data_type": "STRING"},
+                {"name": "password", "data_type": "STRING"},
+                {"name": "rate", "data_type": "INTEGER",
+                 "default_value": "5"},
+            ],
+            "user_defined_script": "ping_rate.py",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "start-stop",
+            "parameter": [
+                {"name": "mgmt_ip", "data_type": "STRING"},
+                {"name": "mgmt_port", "data_type": "INTEGER"},
+                {"name": "username", "data_type": "STRING"},
+                {"name": "password", "data_type": "STRING"},
+                {"name": "start", "data_type": "BOOLEAN",
+                 "default_value": "true"}
+            ],
+            "user_defined_script": "ping_start_stop.py",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        # Add initial config primitive
+        vnf_config.create_initial_config_primitive()
+        init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+            {
+                "seq": 1,
+                "config_primitive_ref": "config",
+            }
+        )
+        vnf_config.initial_config_primitive.append(init_config)
+
+        init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+            {
+                "seq": 2,
+                "config_primitive_ref": "set-rate",
+            },
+        )
+        vnf_config.initial_config_primitive.append(init_config)
+
+        if use_ns_init_conf is False:
+            init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+                {
+                    "seq": 3,
+                    "config_primitive_ref": "start-stop",
+                },
+            )
+            vnf_config.initial_config_primitive.append(init_config)
+
+    def add_pong_config(self, mano_ut=False, use_ns_init_conf=False):
+        vnfd = self.descriptor.vnfd[0]
+        # Add vnf configuration
+        vnf_config = vnfd.vnf_configuration
+
+        # Select "script" configuration
+        vnf_config.script.script_type = 'bash'
+
+        # Add config primitive
+        vnf_config.create_config_primitive()
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "config",
+            "parameter": [
+                {"name": "mgmt_ip", "data_type": "STRING"},
+                {"name": "mgmt_port", "data_type": "INTEGER"},
+                {"name": "username", "data_type": "STRING"},
+                {"name": "password", "data_type": "STRING"},
+                {"name": "service_ip", "data_type": "STRING"},
+                {"name": "service_port", "data_type": "INTEGER"},
+            ],
+            "user_defined_script": "pong_setup.py",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "start-stop",
+            "parameter": [
+                {"name": "mgmt_ip", "data_type": "STRING"},
+                {"name": "mgmt_port", "data_type": "INTEGER"},
+                {"name": "username", "data_type": "STRING"},
+                {"name": "password", "data_type": "STRING"},
+                {"name": "start", "data_type": "BOOLEAN",
+                 "default_value": "true"}
+            ],
+            "user_defined_script": "pong_start_stop.py",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        # Add initial config primitive
+        vnf_config.create_initial_config_primitive()
+        init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+            {
+                "seq": 1,
+                "config_primitive_ref": "config",
+            }
+        )
+        vnf_config.initial_config_primitive.append(init_config)
+
+        if use_ns_init_conf is False:
+            init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+                {
+                    "seq": 2,
+                    "config_primitive_ref": "start-stop",
+                },
+            )
+            vnf_config.initial_config_primitive.append(init_config)
+
     def compose(self, image_name, cloud_init="", cloud_init_file="", endpoint=None, mon_params=[],
                 mon_port=8888, mgmt_port=8888, num_vlr_count=1, num_ivlr_count=1,
-                num_vms=1, image_md5sum=None, mano_ut=False):
+                num_vms=1, image_md5sum=None, mano_ut=False, use_ns_init_conf=False,
+                use_vca_conf=False):
         self.descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog()
         self.id = str(uuid.uuid1())
         vnfd = self.descriptor.vnfd.add()
@@ -202,6 +478,16 @@ class VirtualNetworkFunction(ManoDescriptor):
                 if aws:
                     vdu.cloud_init += "  - [ systemctl, restart, --no-block, elastic-network-interfaces.service ]\n"
 
+            # Add VNF access point
+            if use_vca_conf:
+                self.add_vnf_access_point(mano_ut=mano_ut)
+                if 'pong_' in self.name:
+                    self.add_pong_config(mano_ut=mano_ut,
+                                         use_ns_init_conf=use_ns_init_conf)
+                else:
+                    self.add_ping_config(mano_ut=mano_ut,
+                                         use_ns_init_conf=use_ns_init_conf)
+
             # sepcify the guest EPA
             if use_epa:
                 vdu.guest_epa.trusted_execution = False
@@ -260,7 +546,7 @@ class VirtualNetworkFunction(ManoDescriptor):
                     internal_cp.name = cp_name + "/icp{}".format(i)
                     internal_cp.id = cp_name + "/icp{}".format(i)
                     internal_cp.type_yang = 'VPORT'
-                    ivld_cp = internal_vlds[i].internal_connection_point_ref.add()
+                    ivld_cp = internal_vlds[i].internal_connection_point.add()
                     ivld_cp.id_ref = internal_cp.id
 
                     internal_interface = vdu.internal_interface.add()
@@ -297,16 +583,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)
@@ -314,7 +600,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:
@@ -324,6 +610,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):
@@ -499,8 +814,8 @@ exit 0
             # Config parameters can be taken from config.yaml and
             # actions from actions.yaml in the charm
             # Config to set the home domain
-            vnf_config.create_service_primitive()
-            config = VnfdYang.ServicePrimitive.from_dict({
+            vnf_config.create_config_primitive()
+            config = VnfdYang.ConfigPrimitive.from_dict({
                 "name": "config",
                 "parameter": [
                     {"name": "home_domain", "data_type": "STRING"},
@@ -509,9 +824,9 @@ exit 0
                     {"name": "password", "data_type": "STRING"},
                 ]
             })
-            vnf_config.service_primitive.append(config)
+            vnf_config.config_primitive.append(config)
 
-            config = VnfdYang.ServicePrimitive.from_dict({
+            config = VnfdYang.ConfigPrimitive.from_dict({
                 "name": "create-update-user",
                 # "user-defined-script":"/tmp/test.py",
                 "parameter": [
@@ -519,15 +834,15 @@ exit 0
                     {"name": "password", "data_type": "STRING", "mandatory": True},
                 ]
             })
-            vnf_config.service_primitive.append(config)
+            vnf_config.config_primitive.append(config)
 
-            config = VnfdYang.ServicePrimitive.from_dict({
+            config = VnfdYang.ConfigPrimitive.from_dict({
                 "name": "delete-user",
                 "parameter": [
                     {"name": "number", "data_type": "STRING", "mandatory": True},
                 ]
             })
-            vnf_config.service_primitive.append(config)
+            vnf_config.config_primitive.append(config)
 
     def default_config(self, const_vnfd, vnfd, mano_ut, use_ns_init_conf):
         vnf_config = vnfd.vnfd.vnf_configuration
@@ -559,7 +874,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',
@@ -569,8 +884,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):
@@ -612,10 +927,26 @@ exit 0
                 self.nsd.monitoring_param.append(nsd_monp)
                 param_id += 1
 
+    def add_confparam_map(self):
+        nsd = self.nsd
 
+        confparam_map = nsd.config_parameter_map.add()
+        confparam_map.id = '1'
+        confparam_map.config_parameter_source.member_vnf_index_ref = 2
+        confparam_map.config_parameter_source.config_parameter_source_ref = 'service_ip'
+        confparam_map.config_parameter_request.member_vnf_index_ref = 1
+        confparam_map.config_parameter_request.config_parameter_request_ref = 'pong_ip'
 
+        confparam_map = nsd.config_parameter_map.add()
+        confparam_map.id = '2'
+        confparam_map.config_parameter_source.member_vnf_index_ref = 2
+        confparam_map.config_parameter_source.config_parameter_source_ref = 'service_port'
+        confparam_map.config_parameter_request.member_vnf_index_ref = 1
+        confparam_map.config_parameter_request.config_parameter_request_ref = 'pong_port'
 
-    def compose(self, vnfd_list, cpgroup_list, mano_ut, use_ns_init_conf=True):
+    def compose(self, vnfd_list, cpgroup_list, mano_ut,
+                use_ns_init_conf=True,
+                use_vca_conf=False):
 
         if mano_ut:
             # Disable NS initial config primitive
@@ -678,8 +1009,9 @@ exit 0
                     constituent_vnfd.start_by_default = False
 
                 constituent_vnfd.vnfd_id_ref = vnfd.descriptor.vnfd[0].id
-                self.default_config(constituent_vnfd, vnfd, mano_ut,
-                                    use_ns_init_conf,)
+                if use_vca_conf is False:
+                    self.default_config(constituent_vnfd, vnfd, mano_ut,
+                                        use_ns_init_conf)
                 member_vnf_index += 1
 
         # Enable config primitives if either mano_ut or
@@ -718,6 +1050,8 @@ exit 0
                 member.member_vnf_index_ref = vnfd_index_map[member_vnfd]
 
         # self.create_mon_params(vnfd_list)
+        if use_vca_conf:
+            self.add_confparam_map()
 
     def write_config(self, outdir, vnfds):
 
@@ -837,7 +1171,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):
@@ -892,6 +1226,7 @@ def generate_ping_pong_descriptors(fmt="json",
                                    ex_pong_userdata=None,
                                    use_placement_group=True,
                                    use_ns_init_conf=True,
+                                   use_vca_conf=True,
                                    ):
     # List of connection point groups
     # Each connection point group refers to a virtual link
@@ -936,6 +1271,8 @@ def generate_ping_pong_descriptors(fmt="json",
             num_vms=num_vnf_vms,
             image_md5sum=ping_md5sum,
             mano_ut=mano_ut,
+            use_ns_init_conf=use_ns_init_conf,
+            use_vca_conf=use_vca_conf,
             )
 
     pong = VirtualNetworkFunction("pong_vnfd%s" % (suffix))
@@ -976,6 +1313,8 @@ def generate_ping_pong_descriptors(fmt="json",
             num_vms=num_vnf_vms,
             image_md5sum=pong_md5sum,
             mano_ut=mano_ut,
+            use_ns_init_conf=use_ns_init_conf,
+            use_vca_conf=use_vca_conf,
             )
 
     # Initialize the member VNF index
@@ -1041,11 +1380,15 @@ def generate_ping_pong_descriptors(fmt="json",
     nsd_catalog.compose(vnfd_list,
                         cpgroup_list,
                         mano_ut,
-                        use_ns_init_conf=use_ns_init_conf,)
+                        use_ns_init_conf=use_ns_init_conf,
+                        use_vca_conf=use_vca_conf,
+    )
 
     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 46cad20..cfe58e4 100644 (file)
@@ -108,6 +108,7 @@ module mano-types
         "The value should be greyed out by the UI.
         Only applies to parameters with default values.";
       type boolean;
+      default false;
     }
 
     leaf hidden {
@@ -115,6 +116,13 @@ module mano-types
         "The value should be hidden by the UI.
         Only applies to parameters with default values.";
       type boolean;
+      default false;
+    }
+
+    leaf out {
+      description "If this is an output of the primitive execution";
+      type boolean;
+      default false;
     }
     leaf parameter-type {
       description "Type of this parameter, whether this in IN or OUT";
@@ -281,25 +289,32 @@ module mano-types
         }
       }
 
-      list service-primitive {
-        rwpb:msg-new ServicePrimitive;
+      list config-primitive {
+        rwpb:msg-new ConfigPrimitive;
         description
-          "List of service primitives supported by the
+          "List of config primitives supported by the
           configuration agent for this VNF.";
         key "name";
 
         leaf name {
           description
-            "Name of the service primitive.";
+            "Name of the config primitive.";
           type string;
         }
 
         list parameter {
           description
-            "List of parameters to the service primitive.";
+            "List of parameters to the config primitive.";
           key "name";
           uses primitive-parameter;
         }
+
+        leaf user-defined-script {
+          description
+            "A user defined script. If user defined script is defined,
+             the script will be executed using bash";
+          type string;
+        }
       }
 
       list initial-config-primitive {
@@ -313,20 +328,28 @@ module mano-types
           type uint64;
         }
 
-        leaf name {
-          description 
-              "Name of the configuration primitive.";
-          type string;
-        }
+        choice primtive-type {
+          case primtive-definition {
+            leaf name {
+              description
+                "Name of the configuration primitive.";
+              type string;
+            }
 
-        list parameter {
-          key "name";
-          leaf name {
-            type string;
+            uses primitive-parameter-value;
           }
 
-          leaf value {
-            type string;
+          case primitive-ref {
+            leaf config-primitive-ref {
+              description
+                "Reference to a config primitive name.
+                 NOTE: The config primitive referred should have
+                       all the input paramaters predefined either
+                       with default values or dependency references.";
+              type leafref {
+                path "../../config-primitive/name";
+              }
+            }
           }
         }
       }
index 467be6d..a8c97d4 100644 (file)
@@ -317,46 +317,6 @@ module nsd
         }
       }
 
-      list vnfap-map {
-        key "id";
-        description "A mapping of VNF application point 
-                     capability/dependency within this network service";
-        leaf id {
-          description "Identfier for VNF access point map";
-          type string;
-        }
-        container capability {
-          leaf member-vnf-index-ref {
-            description "Reference to member-vnf within constituent-vnfds";
-            type leafref {
-              path "../../../../constituent-vnfd/member-vnf-index";
-            }
-          }
-          leaf capability-ref {
-            description "Reference to the capability with the capability of the VNF
-                         with the specified member-vnf-index";
-            type leafref {
-              path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:vnfap/vnfd:capability/vnfd:name";
-            }
-          }
-        }
-        container dependency {
-          leaf member-vnf-index-ref {
-            description "Reference to member-vnf within constituent-vnfds";
-            type leafref {
-              path "../../../../constituent-vnfd/member-vnf-index";
-            }
-          }
-          leaf dependency-ref {
-            description "Reference to the dependency within the dependency of the VNF
-                         with the specified member-vnf-index";
-            type leafref {
-              path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:vnfap/vnfd:dependency/vnfd:name";
-            }
-          }
-        }
-      }
-
       // replicate for pnfd container here
       uses manotypes:provider-network;
 
@@ -419,6 +379,46 @@ module nsd
       }
     }
 
+    list config-parameter-map {
+      key "id";
+      description "A mapping of VNF config parameter
+                   requests and sources within this network service";
+      leaf id {
+        description "Identfier for VNF map";
+        type string;
+      }
+      container config-parameter-request {
+        leaf member-vnf-index-ref {
+          description "Reference to member-vnf within constituent-vnfds";
+          type leafref {
+            path "../../../constituent-vnfd/member-vnf-index";
+          }
+        }
+        leaf config-parameter-request-ref {
+          description "Reference to the request in  the VNF
+                       with the specified member-vnf-index";
+          type leafref {
+            path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:config-parameter/vnfd:config-parameter-request/vnfd:name";
+          }
+        }
+      }
+      container config-parameter-source {
+        leaf member-vnf-index-ref {
+          description "Reference to member-vnf within constituent-vnfds";
+          type leafref {
+            path "../../../constituent-vnfd/member-vnf-index";
+          }
+        }
+        leaf config-parameter-source-ref {
+          description "Reference to the source in the VNF
+                       with the specified member-vnf-index";
+          type leafref {
+            path "/vnfd:vnfd-catalog/vnfd:vnfd/vnfd:config-parameter/vnfd:config-parameter-source/vnfd:name";
+          }
+        }
+      }
+    }
+
     list scaling-group-descriptor {
       description
           "scaling group descriptor within this network service.
index 1538341..1c36433 100644 (file)
@@ -115,97 +115,137 @@ module vnfd
     }
   }
 
-  grouping vnf-access-point {
-    list vnfap {
-      description "List of VNF access points";
-      key "name";
-      leaf name {
-        description "Name of the vnf access point";
-        type string {
-          length 128;
-        }
-      }
-      leaf description {
-        description "A description of this VNF access point";
-        type string;
-      }
-      list capability {
-        description "The List  of capabilities of this VNF access point";
+  grouping config-parameter {
+    container config-parameter {
+      rwpb:msg-new ConfigParams;
+      description
+        "List of VNF config parameter requests and sources";
+      list config-parameter-source {
+        description "The list of parameters exposed by this VNF";
         key "name";
+
         leaf name {
-          description "Name of the capability";
+          description "Name of the source";
           type string {
-            length 128;
+            length "1..128";
           }
         }
+
         leaf description {
-          description "A description of the capability";
+          description " Description of the source";
           type string;
         }
-        choice capability-location {
-          case location-path {
-            leaf xpath {
-               description "Location of this capaibility as an xpath.
-                           This field points to an element within vnfd
-                           with the xpath notation and additionally 
-                           provides the flexibility to choose attributes
-                           within the instantiated version of this object.
-                           For example, 
-                              /vnfd-catalog/vnfd[id='xyz']/connection-point[name='cp1']/ip_address()
-                              refers to the ip address of the connection point with name 'cp1' 
-                              in VNFD with id 'xyz'.";
+
+        choice source {
+          case descriptor {
+            leaf descriptor {
+              description
+                "Location of this source as an xpath.
+                 For example:
+                   ../../../mgmt-interface/port";
               type string;
             }
           }
-          case location-primitive-ref {
-            leaf config-primitive-name-ref {
-               description "A leafref to configuration primitive.
-                            This field contains a link to the config primitive
-                            the output paramaters of which will satisfy this requirement.";
-               type leafref {
-                 path "/vnfd:vnfd-catalog/vnfd/vnf-configuration/service-primitive/name";
-               }
+
+          case attirbute {
+            leaf attribute {
+              description
+                "Location of this source as runtime attribute.
+                 The value is <xpath>, <attribute_name>
+                 For example:
+                   ../../../mgmt-interface, ip-address
+                   which retruns the ip-address assigned to the
+                   mgmt-interface after VNF instantiation.";
+              type string;
             }
           }
-          case user-input {
-            list user-input {
-              key "name";
-              description "User input for this capability";
-              leaf name {
-                description "Name of this input";
-                type string;
+
+          case primitive-ref {
+            leaf config-primitive-name-ref {
+              description
+                "A leafref to configuration primitive.
+                 This refers to a config parameter whose
+                 output parameter is referred in out-parameter.";
+              type leafref {
+                path "../../../vnf-configuration/config-primitive/name";
               }
-              leaf value {
-                description "Value of this input";
-                type string;
+            }
+
+            leaf parameter-ref {
+              description
+                "Name of the output parameter in the config primitiive";
+              type leafref {
+                path
+                  "../../../vnf-configuration/config-primitive[name=current()/../config-primitive-name-ref]/parameter/name";
               }
             }
           }
+
+          case value {
+            leaf value {
+              description
+                "Pre-defined value to be used for this source";
+              type string;
+            }
+          }
+        }
+
+        list parameter {
+          key "config-primitive-name-ref";
+
+          leaf config-primitive-name-ref {
+            description
+              "Name of the configuration primitive where this
+             request will used";
+            type leafref {
+              path "../../../../vnf-configuration/config-primitive/name";
+            }
+          }
+
+          leaf config-primitive-parameter-ref {
+            description
+              "Parameter name of the config primitive";
+            type leafref {
+              path "../../../../vnf-configuration/config-primitive[name=current()/../config-primitive-name-ref]/parameter/name";
+            }
+          }
         }
       }
-      list dependency {
-        description "The List of depenencies of this VNF access point";
+
+      list config-parameter-request {
+        description "The list of requests for this VNF";
         key "name";
+
         leaf name {
-          description "Name of this VNF access point";
+          description "Name of this parameter request";
           type string {
-            length 128;
+            length "1..128";
           }
         }
-        leaf mandatory {
-          description "Is this dependcy mandatory for this dependency";
-          type boolean;
-        }
+
         leaf description {
-          description "A description of the dependency";
+          description "Description of this request";
           type string;
         }
-        leaf config-primitive-name-ref {
-         description "A leafref to configuration primitive.
-                      This field contains a link to the config primitive
-                      the input paramaters of which will satisfy this requirement.";
-          type leafref {
-            path "/vnfd:vnfd-catalog/vnfd/vnf-configuration/service-primitive/name";
+
+        list parameter {
+          key "config-primitive-name-ref";
+
+          leaf config-primitive-name-ref {
+            description
+              "Name of the configuration primitive where this
+             request will used";
+            type leafref {
+              path "../../../../vnf-configuration/config-primitive/name";
+            }
+          }
+
+          leaf config-primitive-parameter-ref {
+            description
+              "Parameter name of the config primitive";
+            type leafref {
+              path "../../../../vnf-configuration/config-primitive[name=current()/../config-primitive-name-ref]/parameter/name";
+            }
           }
         }
       }
@@ -252,6 +292,8 @@ module vnfd
 
       uses manotypes:vnf-configuration;
 
+      uses config-parameter;
+
       container mgmt-interface {
         description
             "Interface over which the VNF is managed.";
@@ -606,8 +648,6 @@ module vnfd
       key "id";
 
       uses vnfd-descriptor;
-
-      uses vnf-access-point;
     }
   }
 }
index 8631ae2..8a4c337 100644 (file)
@@ -29,13 +29,10 @@ import rift.cal.rwcal_status as rwcal_status
 import rwlogger
 import neutronclient.common.exceptions as NeutronException
 import keystoneclient.exceptions as KeystoneExceptions
-<<<<<<< 06b6ab4edf863c75fc6e2947ad52d1c76de1a308
 import tornado
 import gi
 
 gi.require_version('RwSdn', '1.0')
-=======
->>>>>>> RIFT-14308 Meaningful logs  while initalizing Openstack driver
 
 from gi.repository import (
     GObject,
index 0400a54..7309d2e 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,134 @@ 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 is None:
+                        # Check if mandatory parameter
+                        if param.mandatory:
+                            msg = "VNFR {}: Primitive {} called " \
+                                  "without mandatory parameter {}". \
+                                  format(vnfr.name, config.name,
+                                         param.name)
+                            self._log.error(msg)
+                            return 1, msg
+
+                    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)
+        self._log.debug("VNFR {} primitive {} exec status: {}".
+                        format(vnfr.name, primitive.name, rc))
+
+        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 +388,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 +464,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 5f33024..a215f67 100644 (file)
@@ -105,7 +105,7 @@ class RiftCMRPCHandler(object):
         if vnf:
             self._log.debug("nsr/vnf {}/{}, vnf_configuration: %s",
                             vnf.vnf_configuration)
-            for primitive in vnf.vnf_configuration.service_primitive:
+            for primitive in vnf.vnf_configuration.config_primitive:
                 if primitive.name == primitive_name:
                     return primitive
 
index 7779479..73a0005 100644 (file)
@@ -298,99 +298,153 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
         return True
 
     @asyncio.coroutine
-    def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output):
-        self._log.debug("jujuCA: VNF config primititve {} for nsr {}, vnfr_id {}".
+    def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive,
+                              vnf_config=None):
+        self._log.debug("jujuCA: VNF config primitive {} for nsr {}, "
+                        "vnfr_id {}".
                         format(primitive, nsr_id, vnfr_id))
+
         try:
             vnfr = self._juju_vnfs[vnfr_id].vnfr
         except KeyError:
-            self._log.error("jujuCA: Did not find VNFR %s in juju plugin",
-                            vnfr_id)
+            msg = "Did not find VNFR %s in Juju plugin", vnfr_id
+            self._log.error(msg)
+            output.execution_error_details = msg
             return
 
-        output.execution_status = "failed"
-        output.execution_id = ''
-        output.execution_error_details = ''
+        if vnf_config is None:
+            vnfr_msg = yield from self.get_vnfr(vnfr_id)
+            if vnfr is None:
+                self._log.error("Unable to get VNFR {} through DTS".
+                                format(vnfr_id))
+                return 'failed', '', "Unable to get VNFR {} through DTS". \
+                    format(vnfr_id)
+
+            vnf_config = vnfr_msg.vnf_configuration
+        self._log.debug("VNF config= %s", vnf_config.as_dict())
 
         try:
             service = vnfr['vnf_juju_name']
-            vnf_config = vnfr['config']
             self._log.debug("VNF config %s", vnf_config)
-            configs = vnf_config.service_primitive
+            configs = vnf_config.config_primitive
             for config in configs:
                 if config.name == primitive.name:
                     self._log.debug("jujuCA: Found the config primitive %s",
                                     config.name)
                     params = {}
-                    for parameter in primitive.parameter:
-                        if parameter.value:
-                            val = self.xlate(parameter.value, vnfr['tags'])
-                            # TBD do validation of the parameters
-                            data_type = 'string'
-                            found = False
-                            for ca_param in config.parameter:
-                                if ca_param.name == parameter.name:
-                                    data_type = ca_param.data_type
-                                    found = True
-                                    break
-                                if data_type == 'integer':
-                                    val = int(parameter.value)
-                            if not found:
-                                self._log.warn("jujuCA: Did not find parameter {} for {}".
-                                               format(parameter, config.name))
+                    for parameter in config.parameter:
+                        val = None
+                        for p in primitive.parameter:
+                            if p.name == parameter.name:
+                                if p.value:
+                                    val = self.xlate(p.value, vnfr['tags'])
+                                break
+
+                        if val is None:
+                            val = parameter.default_value
+
+                        if val is None:
+                            # Check if mandatory parameter
+                            if parameter.mandatory:
+                                msg = "VNFR {}: Primitive {} called " \
+                                      "without mandatory parameter {}". \
+                                      format(vnfr_msg.name, config.name,
+                                             parameter.name)
+                                self._log.error(msg)
+                                return 'failed', '', msg
+
+                        if val:
+                            val = self.convert_value(val, parameter.data_type)
                             params.update({parameter.name: val})
 
+                    rc = ''
+                    exec_id = ''
+                    details = ''
                     if config.name == 'config':
-                        output.execution_id = 'config'
+                        exec_id = 'config'
                         if len(params):
-                            self._log.debug("jujuCA: applying config with params {} for service {}".
+                            self._log.debug("jujuCA: applying config with "
+                                            "params {} for service {}".
                                             format(params, service))
 
-                            rc = yield from self.api.apply_config(params, service=service, wait=False)
+                            rc = yield from self.api.apply_config(
+                                params,
+                                service=service,
+                                wait=False)
 
                             if rc:
                                 # Mark as pending and check later for the status
-                                output.execution_status = "pending"
-                                self._log.debug("jujuCA: applied config {} on {}".
-                                                format(params, service))
+                                rc = "pending"
+                                self._log.debug("jujuCA: applied config {} "
+                                                "on {}".format(params, service))
                             else:
-                                output.execution_status = 'failed'
-                                output.execution_error_details = \
+                                rc = 'failed'
+                                details = \
                                     'Failed to apply config: {}'.format(params)
-                                self._log.error("jujuCA: Error applying config {} on service {}".
+                                self._log.error("jujuCA: Error applying "
+                                                "config {} on service {}".
                                                 format(params, service))
                         else:
-                            self._log.warn("jujuCA: Did not find valid parameters for config : {}".
+                            self._log.warn("jujuCA: Did not find valid "
+                                           "parameters for config : {}".
                                            format(primitive.parameter))
-                            output.execution_status = "completed"
+                            rc = "completed"
                     else:
-                        self._log.debug("jujuCA: Execute action {} on service {} with params {}".
+                        self._log.debug("jujuCA: Execute action {} on "
+                                        "service {} with params {}".
                                         format(config.name, service, params))
 
-                        resp = yield from self.api.execute_action(config.name,
-                                                                  params,
-                                                                  service=service)
+                        resp = yield from self.api.execute_action(
+                            config.name,
+                            params,
+                            service=service)
 
                         if resp:
                             if 'error' in resp:
-                                output.execution_error_details = resp['error']['Message']
+                                details = resp['error']['Message']
                             else:
-                                output.execution_id = resp['action']['tag']
-                                output.execution_status = resp['status']
-                                if output.execution_status == 'failed':
-                                    output.execution_error_details = resp['message']
-                            self._log.debug("jujuCA: execute action {} on service {} returned {}".
-                                            format(config.name, service, output.execution_status))
+                                exec_id = resp['action']['tag']
+                                rc = resp['status']
+                                if rc == 'failed':
+                                    details = resp['message']
+
+                            self._log.debug("jujuCA: execute action {} on "
+                                            "service {} returned {}".
+                                            format(config.name, service, rc))
                         else:
-                            self._log.error("jujuCA: error executing action {} for {} with {}".
-                                            format(config.name, service, params))
-                            output.execution_id = ''
-                            output.execution_status = 'failed'
-                            output.execution_error_details = "Failed to queue the action"
+                            self._log.error("jujuCA: error executing action "
+                                            "{} for {} with {}".
+                                            format(config.name, service,
+                                                   params))
+                            exec_id = ''
+                            rc = 'failed'
+                            details = "Failed to queue the action"
                     break
 
         except KeyError as e:
-            self._log.info("VNF %s does not have config primititves, e=%s", vnfr_id, e)
+            msg = "VNF %s does not have config primitives, e=%s", \
+                  vnfr_msg.name, e
+            self._log.error(msg)
+            raise ValueError(msg)
+
+        return rc, exec_id, details
+
+    @asyncio.coroutine
+    def vnf_config_primitive(self, nsr_id, vnfr_id, primitive, output):
+        output.execution_status = "failed"
+        output.execution_id = ''
+        output.execution_error_details = ''
+
+        rc, exec_id, err = yield from self._vnf_config_primitive(
+            nsr_id,
+            vnfr_id,
+            primitive)
+
+        self._log.debug("VNFR {} primitive {} exec status: {}".
+                        format(vnfr.name, primitive.name, rc))
+        output.execution_status = rc
+        output.execution_id = exec_id
+        output.execution_error_details = err
 
     @asyncio.coroutine
     def apply_config(self, agent_nsr, agent_vnfr, config, rpc_ip):
@@ -497,76 +551,126 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
         Actions in initial config may not work based on charm design
         """
 
-        vnfr = agent_vnfr.vnfr
-        service = vnfr['vnf_juju_name']
+        try:
+            vnfr = self._juju_vnfs[agent_vnfr.id].vnfr
+        except KeyError:
+            self._log.error("Did not find VNFR %s in Juju plugin",
+                            agent_vnfr.name)
+            return False
+
+        vnfr_msg = yield from self.get_vnfr(agent_vnfr.id)
+        if vnfr_msg 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_msg.vnf_configuration
+        self._log.debug("VNFR %s config: %s", vnfr_msg.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_msg.name))
+            return True
 
+        service = vnfr['vnf_juju_name']
         rc = yield from self.api.is_service_up(service=service)
         if not rc:
             return False
 
         action_ids = []
         try:
-            vnf_cat = agent_vnfr.vnfr_msg
-            if vnf_cat and vnf_cat.mgmt_interface.ip_address:
-                vnfr['tags'].update({'rw_mgmt_ip': vnf_cat.mgmt_interface.ip_address})
+            if vnfr_msg.mgmt_interface.ip_address:
+                vnfr['tags'].update({'rw_mgmt_ip': vnfr_msg.mgmt_interface.ip_address})
                 self._log.debug("jujuCA:(%s) tags: %s", vnfr['vnf_juju_name'], vnfr['tags'])
 
-            config = {}
-            try:
-                for primitive in vnfr['config'].initial_config_primitive:
-                    self._log.debug("jujuCA:(%s) Initial config primitive %s", vnfr['vnf_juju_name'], primitive)
+            for primitive in primitives:
+                self._log.debug("(%s) Initial config primitive %s",
+                                vnfr['vnf_juju_name'], primitive)
+                if primitive.config_primitive_ref:
+                    # Reference to a primitive in config primitive
+                    class Primitive:
+                        def __init__(self, name):
+                            self.name = name
+                            self.value = None
+                            self.parameter = []
+
+                    prim = Primitive(primitive.config_primitive_ref)
+                    rc, eid, err = yield from self._vnf_config_primitive(
+                        agent_nsr.id,
+                        agent_vnfr.id,
+                        prim,
+                        vnf_config)
+
+                    if rc == "failed":
+                        msg = "Error executing initial config primitive" \
+                              " {} in VNFR {}: rc={}, stderr={}". \
+                              format(prim.name, vnfr_msg.name, rc, err)
+                        self._log.error(msg)
+                        return False
+
+                    elif rc == "pending":
+                        action_ids.append(eid)
+
+                elif primitive.name:
+                    config = {}
                     if primitive.name == 'config':
                         for param in primitive.parameter:
                             if vnfr['tags']:
-                                val = self.xlate(param.value, vnfr['tags'])
+                                val = self.xlate(param.value,
+                                                 vnfr['tags'])
                                 config.update({param.name: val})
-            except KeyError as e:
-                self._log.exception("jujuCA:(%s) Initial config error(%s): config=%s",
-                                    vnfr['vnf_juju_name'], str(e), config)
-                config = None
-                return False
 
-            if config:
-                self.juju_log('info', vnfr['vnf_juju_name'],
-                              "Applying Initial config:%s",
-                              config)
+                        if config:
+                            self.juju_log('info', vnfr['vnf_juju_name'],
+                                          "Applying Initial config:%s",
+                                          config)
 
-                rc = yield from self.api.apply_config(config, service=service)
-                if rc is False:
-                    self.log.error("Service {} is in error state".format(service))
-                    return False
-
-
-            # Apply any actions specified as part of initial config
-            for primitive in vnfr['config'].initial_config_primitive:
-                if primitive.name != 'config':
-                    self._log.debug("jujuCA:(%s) Initial config action primitive %s",
-                                    vnfr['vnf_juju_name'], primitive)
-                    action = primitive.name
-                    params = {}
-                    for param in primitive.parameter:
-                        val = self.xlate(param.value, vnfr['tags'])
-                        params.update({param.name: val})
-
-                    self._log.info("jujuCA:(%s) Action %s with params %s",
-                                   vnfr['vnf_juju_name'], action, params)
-
-                    resp = yield from self.api.execute_action(action, params,
-                                                              service=service)
-                    if 'error' in resp:
-                        self._log.error("Applying initial config on {} failed for {} with {}: {}".
-                                        format(vnfr['vnf_juju_name'], action, params, resp))
-                        return False
+                            rc = yield from self.api.apply_config(
+                                config,
+                                service=service)
+                            if rc is False:
+                                self.log.error("Service {} is in error state".
+                                               format(service))
+                                return False
 
-                    action_ids.append(resp['action']['tag'])
+                    # Apply any actions specified as part of initial config
+                    else:
+                        self._log.debug("(%s) Initial config action "
+                                        "primitive %s",
+                                        vnfr['vnf_juju_name'], primitive)
+                        action = primitive.name
+                        params = {}
+                        for param in primitive.parameter:
+                            val = self.xlate(param.value, vnfr['tags'])
+                            params.update({param.name: val})
+
+                            self._log.info("(%s) Action %s with params %s",
+                                           vnfr['vnf_juju_name'], action,
+                                           params)
+
+                        resp = yield from self.api.execute_action(
+                            action,
+                            params,
+                            service=service)
+                        if 'error' in resp:
+                            self._log.error("Applying initial config on {}"
+                                            " failed for {} with {}: {}".
+                                            format(vnfr['vnf_juju_name'],
+                                                   action, params, resp))
+                            return False
+
+                        action_ids.append(resp['action']['tag'])
 
-        except KeyError as e:
-            self._log.info("Juju config agent(%s): VNFR %s not managed by Juju",
-                           vnfr['vnf_juju_name'], agent_vnfr.id)
-            return False
         except Exception as e:
-            self._log.exception("jujuCA:(%s) Exception juju apply_initial_config for VNFR {}: {}".
-                                format(vnfr['vnf_juju_name'], agent_vnfr.id, e))
+            self._log.exception("jujuCA:(%s) Exception juju "
+                                "apply_initial_config for VNFR {}: {}".
+                                format(vnfr['vnf_juju_name'],
+                                       agent_vnfr.id, e))
             return False
 
         # Check if all actions completed
@@ -576,11 +680,13 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
             for act in action_ids:
                 resp = yield from self.api.get_action_status(act)
                 if 'error' in resp:
-                    self._log.error("Initial config failed: {}".format(resp))
+                    self._log.error("Initial config failed for action {}: {}".
+                                    format(act, resp))
                     return False
 
                 if resp['status'] == 'failed':
-                    self._log.error("Initial config action failed: {}".format(resp))
+                    self._log.error("Initial config action failed for "
+                                    "action {}: {}".format(act, resp))
                     return False
 
                 if resp['status'] == 'pending':
index f285afd..78f0aa0 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
@@ -57,6 +88,10 @@ class RiftCMnsr(object):
     def nsr_cfg_msg(self):
         return self._cfg
 
+    @property
+    def nsd(self):
+        return self._cfg.nsd
+
     @property
     def job_id(self):
         ''' Get a new job id for config primitive'''
@@ -71,11 +106,11 @@ class RiftCMnsr(object):
     def member_vnf_index(self):
         return self._vnfr['member_vnf_index_ref']
 
-    def add_vnfr(self, vnfr, vnfr_msg):
+    def add_vnfr(self, vnfr, vnfr_msg, vnfd):
         if vnfr['id'] in self._vnfr_ids.keys():
             agent_vnfr = self._vnfr_ids[vnfr['id']]
         else:
-            agent_vnfr = RiftCMvnfr(self.name, vnfr, vnfr_msg)
+            agent_vnfr = RiftCMvnfr(self.name, vnfr, vnfr_msg, vnfd)
             self._vnfrs.append(agent_vnfr)
             self._vnfrs_msg.append(vnfr_msg)
             self._vnfr_ids[agent_vnfr.id] = agent_vnfr
@@ -85,13 +120,20 @@ 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
     '''
-    def __init__(self, nsr_name, vnfr_dict, vnfr_msg):
+    def __init__(self, nsr_name, vnfr_dict, vnfr_msg, vnfd):
         self._vnfr = vnfr_dict
         self._vnfr_msg = vnfr_msg
+        self._vnfd_msg = vnfd
         self._nsr_name = nsr_name
         self._configurable = False
 
@@ -107,6 +149,10 @@ class RiftCMvnfr(object):
     def vnfr_msg(self):
         return self._vnfr_msg
 
+    @property
+    def vnfd(self):
+        return self._vnfd_msg
+
     @property
     def name(self):
         return self._vnfr['short_name']
@@ -275,6 +321,119 @@ 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:
+                if rc == 0:
+                    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:
@@ -313,6 +472,8 @@ class RiftCMConfigPluginBase(object):
                 self._log.error("Unknown method %s invoked on config agent plugin",
                                 method)
         except Exception as e:
-            self._log.error("Caught exception while invoking method: %s, Exception: %s", method, str(e))
-            raise
+            self._log.exception("Caught exception while invoking method: %s, "
+                                "Exception: %s", method, str(e))
+            raise e
+
         return rc
index 543e51b..7fa5042 100644 (file)
@@ -133,9 +133,9 @@ class RiftCMConfigAgent(object):
                 rc = yield from agent.invoke(method, nsr, vnfr, *args)
                 break
             except Exception as e:
-                self._log.error("Error invoking {} on {} : {}".
-                                format(method, agent.name, e))
-                raise
+                self._log.exception("Error invoking {} on {} : {}".
+                                    format(method, agent.name, e))
+                raise e
 
         self._log.info("vnfr({}), method={}, return rc={}"
                        .format(vnfr.name, method, rc))
index 4848e9e..bc13b17 100644 (file)
@@ -205,7 +205,7 @@ class ConfigManagerConfig(object):
                 done = False
                 if 'nsrid' in task:
                     nsrid = task['nsrid']
-                    self._log.debug("Will execute pending task for NSR id(%s)", nsrid)
+                    self._log.debug("Will execute pending task for NSR id: %s", nsrid)
                     try:
                         # Try to configure this NSR
                         task['retries'] -= 1
@@ -220,7 +220,7 @@ class ConfigManagerConfig(object):
                         self.pending_tasks.remove(task)
 
                     if done:
-                        self._log.debug("Finished pending task NSR id(%s):", nsrid)
+                        self._log.debug("Finished pending task NSR id: %s", nsrid)
                     else:
                         self._log.error("Failed configuring NSR(%s), retries remained:%d!",
                                         nsrid, task['retries'])
@@ -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):
 
@@ -292,16 +292,16 @@ class ConfigManagerConfig(object):
         except Exception as e:
             self._log.error("Failed to register for opdata as (%s)", e)
 
+    def get_config_method(self, vnf_config):
+        cfg_types = ['netconf', 'juju', 'script']
+        for method in cfg_types:
+            if method in vnf_config:
+                return method
+        return None
+
     @asyncio.coroutine
     def process_nsd_vnf_configuration(self, nsr_obj, vnfr):
 
-        def get_config_method(vnf_config):
-            cfg_types = ['netconf', 'juju', 'script']
-            for method in cfg_types:
-                if method in vnf_config:
-                    return method
-            return None
-            
         def get_cfg_file_extension(method,  configuration_options):
             ext_dict = {
                 "netconf" : "xml",
@@ -349,6 +349,9 @@ class ConfigManagerConfig(object):
             target : running
         '''
 
+        # Get vnf_configuration from vnfr
+        vnf_config = vnfr['vnf_configuration']
+
         # Save some parameters needed as short cuts in flat structure (Also generated)
         vnf_cfg = vnfr['vnf_cfg']
         # Prepare unique name for this VNF
@@ -360,9 +363,6 @@ class ConfigManagerConfig(object):
         nsr_vnfr = '{}/{}_{}'.format(
             vnf_cfg['nsr_name'], vnfr['short_name'], vnfr['member_vnf_index_ref'])
 
-        # Get vnf_configuration from vnfr
-        vnf_config = vnfr['vnf_configuration']
-
         self._log.debug("vnf_configuration = %s", vnf_config)
 
         # Create priority dictionary
@@ -375,7 +375,7 @@ class ConfigManagerConfig(object):
             # No VNFR with this priority yet, initialize the list
             nsr_obj.nsr_cfg_config_attributes_dict[cfg_priority_order] = []
 
-        method = get_config_method(vnf_config)
+        method = self.get_config_method(vnf_config)
         if method is not None:
             # Create all sub dictionaries first
             config_priority = {
@@ -384,9 +384,12 @@ class ConfigManagerConfig(object):
                 'member_vnf_index' : vnfr['member_vnf_index_ref'],
             }
 
-            if 'config_delay' in vnf_config['config_attributes']:
-                config_priority['configuration_delay'] = vnf_config['config_attributes']['config_delay']
-                vnf_cfg['config_delay'] = config_priority['configuration_delay']
+            if ('config_attributes' in vnf_config) and \
+               ('config_delay' in vnf_config['config_attributes']):
+                    config_priority['configuration_delay'] = vnf_config['config_attributes']['config_delay']
+                    vnf_cfg['config_delay'] = config_priority['configuration_delay']
+            else:
+                vnf_cfg['config_delay'] = 0
 
             configuration_options = {}
             self._log.debug("config method=%s", method)
@@ -416,16 +419,14 @@ class ConfigManagerConfig(object):
             vnf_cp_dict['rw_mgmt_ip'] = vnf_cfg['mgmt_ip_address']
             vnf_cp_dict['rw_username'] = vnf_cfg['username']
             vnf_cp_dict['rw_password'] = vnf_cfg['password']
-            
 
-            # TBD - see if we can neatly include the config in "config_attributes" file, no need though
-            #config_priority['config_template'] = vnf_config['config_template']
-            # Create config file
-            vnf_cfg['juju_script'] = os.path.join(self._parent.cfg_dir, 'juju_if.py')
 
             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
@@ -456,6 +457,338 @@ class ConfigManagerConfig(object):
         # Update the cm-state
         nsr_obj.populate_vm_state_from_vnf_cfg()
 
+    @asyncio.coroutine
+    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'])
+            srcs = cm_state['config_parameter']['config_parameter_source']
+            reqs = cm_state['config_parameter']['config_parameter_request']
+
+            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 src in srcs:
+                        for p in src['parameter']:
+                            if (p['config_primitive_ref'] == confp['name']) \
+                               and (p['parameter_ref'] == param['name']):
+                                param['default_value'] = src['value']
+                                param['read_only'] = True
+                                found = True
+                                break
+                        if found:
+                            break
+
+                    if not found:
+                        for req in reqs:
+                            for p in req['parameter']:
+                                if (p['config_primitive_ref'] == confp['name']) \
+                                   and (p['parameter_ref'] == param['name']):
+                                    param['default_value'] = req['value']
+                                    param['read_only'] = True
+                                    found = True
+                                    break
+                            if found:
+                                break
+
+                self._log.debug("Config primitive: {}".format(confp))
+                vnf_configuration['config_primitive'].append(confp)
+
+            cm_state['vnf_configuration'] = vnf_configuration
+
+    @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("VNF {}, Did not find the xpath specified: {}".
+                                     format(vnf_name, xpath))
+                prefix = prefix[:idx]
+                xp = xp[3:]
+
+            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("Invalid xpath {} for source {} in VNF {}".
+                            format(xpath, name, vnf_name))
+            raise ValueError("Descriptor xpath {} in source {} 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='{}']/config-parameter" \
+                "/config-parameter-source/config-parameter-source[name='{}']" \
+                "/descriptor".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 {}, descriptor 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)
+        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 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='{}']/config_parameter" \
+                "/config-parameter-source/config-parameter-source[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 request {} 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.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 source {} in VNF {}".
+                                 format(attr, dts_path, vnfd.name))
+
+        self._log.debug("Attribute {}: {}".format(attribute, val))
+        return val
+
+    @asyncio.coroutine
+    def process_vnf_config_parameter(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
+
+            try:
+                cparam = vnfd.config_parameter
+            except AttributeError as e:
+                self._log.debug("VNFR {} does not have VNF config parameter".
+                                format(vnfr.name))
+                continue
+
+            srcs = []
+            try:
+                srcs = cparam.config_parameter_source
+            except AttributeError as e:
+                self._log.debug("VNFR {} has no source defined".
+                                format(vnfr.name))
+
+            # Get the cm state dict for this vnfr
+            cm_state = nsr_obj.find_vnfr_cm_state(vnfr['id'])
+
+            cm_srcs = []
+            for src in srcs:
+                self._log.debug("VNFR {}: source {}".
+                                format(vnfr['name'], src.as_dict()))
+
+                param_refs = []
+                for p in src.parameter:
+                    param_refs.append({
+                        'config_primitive_ref': p.config_primitive_name_ref,
+                        'parameter_ref': p.config_primitive_parameter_ref
+                    })
+
+                try:
+                    val = src.value
+                    self._log.debug("Got value {}".format(val))
+                    if val:
+                        cm_srcs.append({'name': src.name,
+                                        'value': str(val),
+                                        'parameter': param_refs})
+                        continue
+                except AttributeError as e:
+                    pass
+
+                try:
+                    xpath = src.descriptor
+                    # resolve xpath
+                    if xpath:
+                        val = yield from self.resolve_xpath(xpath, src.name, vnfd)
+                        self._log.debug("Got xpath value: {}".format(val))
+                        cm_srcs.append({'name': src.name,
+                                        'value': str(val),
+                                        'parameter': param_refs})
+                        continue
+                except AttributeError as e:
+                    pass
+
+                try:
+                    attribute = src.attribute
+                    # resolve attribute
+                    if attribute:
+                        val = yield from self.resolve_attribute(attribute,
+                                                                src.name,
+                                                                vnfd, vnfr)
+                        self._log.debug("Got attribute value: {}".format(val))
+                        cm_srcs.append({'name': src.name,
+                                        'value': str(val),
+                                        'parameter': param_refs})
+                        continue
+                except AttributeError as e:
+                    pass
+
+                try:
+                    prim = src.primitive_ref
+                    if prim:
+                        raise NotImplementedError("{}: VNF config parameter {}"
+                                                  "source support for config"
+                                                  "primitive not yet supported".
+                                                  format(vnfr.name, prim))
+                except AttributeError as e:
+                    pass
+
+            self._log.debug("VNF config parameter sources: {}".format(cm_srcs))
+            cm_state['config_parameter']['config_parameter_source'] = cm_srcs
+
+            try:
+                reqs = cparam.config_parameter_request
+            except AttributeError as e:
+                self._log.debug("VNFR {} has no requests defined".
+                                format(vnfr.name))
+                continue
+
+            cm_reqs = []
+            for req in reqs:
+                self._log.debug("VNFR{}: request {}".
+                                format(vnfr['name'], req.as_dict()))
+                param_refs = []
+                for p in req.parameter:
+                    param_refs.append({
+                        'config_primitive_ref': p.config_primitive_name_ref,
+                        'parameter_ref': p.config_primitive_parameter_ref
+                    })
+                cm_reqs.append({'name': req.name,
+                                'parameter': param_refs})
+
+            self._log.debug("VNF requests: {}".format(cm_reqs))
+            cm_state['config_parameter']['config_parameter_request'] = cm_reqs
+
+        # Publish all config parameter for the VNFRs
+        # yield from nsr_obj.publish_cm_state()
+
+        cparam_map = []
+        try:
+            cparam_map = nsd.config_parameter_map
+        except AttributeError as e:
+            self._log.debug("No config parameter map specified for nsr: {}".
+                            format(nsr_obj.nsr_name))
+
+        for cp in cparam_map:
+            src_vnfr = nsr_obj.agent_nsr.get_member_vnfr(
+                cp.config_parameter_source.member_vnf_index_ref)
+            cm_state = nsr_obj.find_vnfr_cm_state(src_vnfr.id)
+            if cm_state is None:
+                raise ValueError("Config parameter sources are not defined "
+                        "for VNF member {} ({})".
+                        format(cp.config_parameter_source.member_vnf_index_ref,
+                               src_vnfr.name))
+            srcs = cm_state['config_parameter']['config_parameter_source']
+
+            src_attr = cp.config_parameter_source.config_parameter_source_ref
+            val = None
+            for src in srcs:
+                if src['name'] == src_attr:
+                    val = src['value']
+                    break
+
+            req_vnfr = nsr_obj.agent_nsr.get_member_vnfr(
+                cp.config_parameter_request.member_vnf_index_ref)
+            req_attr = cp.config_parameter_request.config_parameter_request_ref
+            cm_state = nsr_obj.find_vnfr_cm_state(req_vnfr.id)
+            try:
+                cm_reqs = cm_state['config_parameter']['config_parameter_request']
+            except KeyError as e:
+                raise ValueError("VNFR index {} ({}) has no requests defined".
+                        format(cp.config_parameter_reequest.member_vnf_index_ref,
+                               req_vnfr['name']))
+
+            for i, item in enumerate(cm_reqs):
+                if item['name'] == req_attr:
+                    item['value'] = str(val)
+                    cm_reqs[i] = item
+                    self._log.debug("Request in VNFR {}: {}".
+                                    format(req_vnfr.name, item))
+                    break
+
+        yield from self.update_config_primitives(nsr_obj)
+
+        # Publish resolved dependencies for the VNFRs
+        yield from nsr_obj.publish_cm_state()
+
     @asyncio.coroutine
     def config_NSR(self, id):
 
@@ -463,14 +796,14 @@ class ConfigManagerConfig(object):
 
             yaml_dict = dict(sorted(config_attributes_dict.items()))
             yf.write(yaml.dump(yaml_dict, default_flow_style=False))
-        
+
         nsr_dict = self._nsr_dict
         self._log.info("Configure NSR, id = %s", id)
 
         #####################TBD###########################
         # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_create_nsr', self.id, self._nsd)
         # yield from self._config_agent_mgr.invoke_config_agent_plugins('notify_nsr_active', self.id, self._vnfrs)
-        
+
         try:
             if id not in nsr_dict:
                 nsr_obj = ConfigManagerNSR(self._log, self._loop, self, id)
@@ -486,7 +819,7 @@ class ConfigManagerConfig(object):
         if nsr_obj.cm_nsr['state'] != nsr_obj.state_to_string(conmanY.RecordState.INIT):
             self._log.debug("NSR(%s) is already processed, state=%s",
                             nsr_obj.nsr_name, nsr_obj.cm_nsr['state'])
-            yield from nsr_obj.publish_cm_state()
+            #yield from nsr_obj.publish_cm_state()
             return True
 
         cmdts_obj = self.cmdts_obj
@@ -519,22 +852,31 @@ class ConfigManagerConfig(object):
                         # return
 
                     nsr_obj.set_config_dir(self)
-                    
+
                     for const_vnfr in nsr['constituent_vnfr_ref']:
                         self._log.debug("Fetching VNFR (%s)", const_vnfr['vnfr_id'])
                         vnfr_msg = yield from cmdts_obj.get_vnfr(const_vnfr['vnfr_id'])
                         if vnfr_msg:
                             vnfr = vnfr_msg.as_dict()
+                            # Get the VNFD also
+                            vnfd = vnfr_msg.vnfd
+
                             self._log.info("create VNF:{}/{}".format(nsr_obj.nsr_name, vnfr['short_name']))
-                            agent_vnfr = yield from nsr_obj.add_vnfr(vnfr, vnfr_msg)
+                            agent_vnfr = yield from nsr_obj.add_vnfr(vnfr, vnfr_msg, vnfd)
 
                             # Preserve order, self.process_nsd_vnf_configuration()
                             # sets up the config agent based on the method
                             yield from self.process_nsd_vnf_configuration(nsr_obj, vnfr)
-                            yield from self._config_agent_mgr.invoke_config_agent_plugins(
-                                'notify_create_vnfr',
-                                nsr_obj.agent_nsr,
-                                agent_vnfr)
+
+                    # Process VNF config parameter
+                    yield from self.process_vnf_config_parameter(nsr_obj)
+
+                    # Invoke the config agent plugin
+                    for vnfr in nsr_obj.vnfrs:
+                        yield from self._config_agent_mgr.invoke_config_agent_plugins(
+                            'notify_create_vnfr',
+                            nsr_obj.agent_nsr,
+                            vnfr['vnf_cfg']['agent_vnfr'])
 
                         #####################TBD###########################
                         # self._log.debug("VNF active. Apply initial config for vnfr {}".format(vnfr.name))
@@ -583,7 +925,7 @@ class ConfigManagerConfig(object):
                         # Iterate through each priority level
                         for vnf_config_attributes_dict in config_attributes_dict:
                             # Iterate through each vnfr at this priority level
-                                
+
                             # Make up vnf_unique_name with vnfd name and member index
                             #vnfr_name = "{}.{}".format(nsr_obj.nsr_name, vnf_config_attributes_dict['name'])
                             vnf_unique_name = get_vnf_unique_name(
@@ -596,7 +938,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
@@ -643,7 +986,8 @@ class ConfigManagerConfig(object):
                 if nsr_obj_p == nsr_obj:
                     assert id == nsr_obj_p._nsr_id
                     #self._parent.pending_cfg.remove(nsr_obj_p)
-                    # Mark this as being deleted so we do not try to configure it if we are in cfg_delay (will wake up and continue to process otherwise)
+                    # Mark this as being deleted so we do not try to configure it
+                    # if we are in cfg_delay (will wake up and continue to process otherwise)
                     nsr_obj_p.being_deleted = True
                     self._log.info("Removed scheduled configuration for NSR(%s)", nsr_obj.nsr_name)
 
@@ -802,12 +1146,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)
@@ -905,7 +1255,7 @@ class ConfigManagerNSR(object):
                             caller._nsr['name_ref'], self.this_nsr_dir)
         self.config_attributes_file = os.path.join(self.this_nsr_dir, "configuration_config_attributes.yml")
         self.xlate_dict_file = os.path.join(self.this_nsr_dir, "nsr_xlate_dict.yml")
-        
+
     def xlate_conf(self, vnfr, vnf_cfg):
 
         # If configuration type is not already set, try to read from attributes
@@ -964,30 +1314,31 @@ 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))
+
+                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, 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)
@@ -1113,6 +1464,11 @@ class ConfigManagerNSR(object):
                 'cfg_type' : vnf_cfg['config_method'],
                 'cfg_location' : vnf_cfg['cfg_file'],
                 'connection_point' : [],
+                'config_parameter' :
+                {
+                    'config_parameter_source' : [],
+                    'config_parameter_request' : [],
+                },
             }
             self.cm_nsr['cm_vnfr'].append(vnf_cm_state)
 
@@ -1172,7 +1528,7 @@ class ConfigManagerNSR(object):
             yield from self.publish_cm_state()
 
     @asyncio.coroutine
-    def add_vnfr(self, vnfr, vnfr_msg):
+    def add_vnfr(self, vnfr, vnfr_msg, vnfd):
 
         @asyncio.coroutine
         def populate_subnets_from_vlr(id):
@@ -1188,13 +1544,14 @@ class ConfigManagerNSR(object):
             except Exception as e:
                 self._log.error("VNF:(%s) VLR Error = %s",
                                 vnfr['short_name'], e)
-            
+
         if vnfr['id'] not in self._vnfr_dict:
             self._log.info("NSR(%s) : Adding VNF Record for name=%s, id=%s", self._nsr_id, vnfr['short_name'], vnfr['id'])
             # Add this vnfr to the list for show, or single traversal
             self._vnfr_list.append(vnfr)
         else:
-            self._log.warning("NSR(%s) : VNF Record for name=%s, id=%s already exists, overwriting", self._nsr_id, vnfr['short_name'], vnfr['id'])
+            self._log.warning("NSR(%s) : VNF Record for name=%s, id=%s already exists, overwriting",
+                              self._nsr_id, vnfr['short_name'], vnfr['id'])
 
         # Make vnfr available by id as well as by name
         unique_name = get_vnf_unique_name(self.nsr_name, vnfr['short_name'], vnfr['member_vnf_index_ref'])
@@ -1205,7 +1562,7 @@ class ConfigManagerNSR(object):
         vnf_cfg = {
             'nsr_obj' : self,
             'vnfr' : vnfr,
-            'agent_vnfr' : self.agent_nsr.add_vnfr(vnfr, vnfr_msg),
+            'agent_vnfr' : self.agent_nsr.add_vnfr(vnfr, vnfr_msg, vnfd),
             'nsr_name' : self.nsr_name,
             'nsr_id' : self._nsr_id,
             'vnfr_name' : vnfr['short_name'],
@@ -1270,7 +1627,7 @@ class ConfigManagerNSR(object):
         if 'internal_vlr' in vnfr:
             for ivlr in vnfr['internal_vlr']:
                 yield from populate_subnets_from_vlr(ivlr['vlr_ref'])
-                
+
         # Update vnfr
         vnf_cfg['agent_vnfr']._vnfr = vnfr
         return vnf_cfg['agent_vnfr']
@@ -1292,6 +1649,11 @@ class XPaths(object):
         return ("D,/vnfr:vnfr-catalog/vnfr:vnfr" +
                 ("[vnfr:id='{}']".format(k) if k is not None else ""))
 
+    @staticmethod
+    def vnfd_path(k=None):
+        return ("C,/vnfd:vnfd-catalog/vnfd:vnfd" +
+                ("[vnfd:id='{}']".format(k) if k is not None else ""))
+
     @staticmethod
     def config_agent(k=None):
         return ("D,/rw-config-agent:config-agent/rw-config-agent:account" +
@@ -1334,6 +1696,15 @@ class ConfigManagerDTS(object):
         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)
@@ -1377,6 +1748,15 @@ class ConfigManagerDTS(object):
             vnfr_msg = vnfrl[0]
         return vnfr_msg
 
+    @asyncio.coroutine
+    def get_vnfd(self, id):
+        self._log.debug("Attempting to get VNFD: %s", XPaths.vnfd_path(id))
+        vnfdl = yield from self._read_dts(XPaths.vnfd_path(id), do_trace=False)
+        vnfd_msg = None
+        if len(vnfdl) > 0:
+            vnfd_msg = vnfdl[0]
+        return vnfd_msg
+
     @asyncio.coroutine
     def get_vlr(self, id):
         self._log.debug("Attempting to get VLR subnet: %s", id)
@@ -1414,7 +1794,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 7ea73c4..376814b 100755 (executable)
@@ -225,8 +225,8 @@ class ConfigurationManager(object):
                     ids.append(nsr_id)
                     e = task.exception()
                     if e:
-                        self._log.error("Exception in configuring nsr {}: {}".
-                                        format(nsr_id, e))
+                        self._log.exception("Exception in configuring nsr {}: {}".
+                                            format(nsr_id, e))
                         nsr_obj = self.get_nsr_obj(nsr_id)
                         if nsr_obj:
                             yield from nsr_obj.update_ns_cm_state(conmanY.RecordState.CFG_FAILED, str(e))
index bb1555d..daaa8d0 100644 (file)
@@ -253,6 +253,70 @@ module rw-conman
             type inet:ip-address;
           }
         }
+        // Publish the resolved capabilites and dependecies here
+        container config-parameter {
+          description
+            "The resolved config parameter for a VNF";
+          list config-parameter-source {
+            description "List of resolved sources";
+            key "name";
+
+            leaf name {
+              type string {
+                length 128;
+              }
+              description
+                "Name of the source as defined in VNFD";
+            }
+            leaf value {
+              type string;
+              description
+                "Resovled value for the source";
+            }
+            list parameter {
+              key "config-primitive-ref";
+              leaf config-primitive-ref {
+                type string;
+              }
+              leaf parameter-ref {
+                type string;
+              }
+            }
+          }
+
+          list config-parameter-request {
+            description
+              "List of resolved requests for the the VNF";
+            key "name";
+
+            leaf name {
+              type string {
+                length 128;
+              }
+              description
+                "Name of the request as defined in VNFD";
+            }
+            leaf value {
+              type string;
+              description
+                "Resovled value for the request";
+            }
+            list parameter {
+              key "config-primitive-ref";
+              leaf config-primitive-ref {
+                type string;
+              }
+              leaf parameter-ref {
+                type string;
+              }
+            }
+          }
+        }
+
+        // Resolve the VNF config-primitives and publish it
+        // here. VNFR will use this and update config-primitives
+        // from here.
+        uses manotypes:vnf-configuration;
       } // list VNFR
     } // list NSR
   } // cm-state
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 2388b91..16431be 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,
 )
 
@@ -830,7 +832,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):
@@ -981,7 +986,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
                 )
 
@@ -1042,6 +1047,49 @@ 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 pa.default_value and \
+                                   (pa.default_value != param.default_value):
+                                    param.default_value = pa.default_value
+                                    param.read_only = pa.read_only
+                                    updated = True
+                                break
+                    self._log.debug("Prim: {}".format(prim.as_dict()))
+                    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 7052299..87f497d 100755 (executable)
@@ -1336,6 +1336,9 @@ class VirtualNetworkFunctionRecord(object):
         vnfr_dict.update(vnfd_copy_dict)
 
         vnfr_msg = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict(vnfr_dict)
+        vnfr_msg.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict(self.vnfd.as_dict())
+
+        vnfr_msg.create_time = self._create_time
         vnfr_msg.uptime = int(time.time()) - self._create_time
         vnfr_msg.mgmt_interface = mgmt_intf
 
@@ -1375,6 +1378,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()
@@ -2229,9 +2243,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(
@@ -2251,7 +2264,6 @@ class VnfrDtsHandler(object):
                                         handler=hdl,
                                         flags=(rwdts.Flag.PUBLISHER |
                                                rwdts.Flag.NO_PREP_READ |
-                                               rwdts.Flag.CACHE |
                                                rwdts.Flag.DATASTORE),)
 
     @asyncio.coroutine
@@ -2267,13 +2279,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)
 
index 69a0d40..a4ef59d 100755 (executable)
@@ -642,7 +642,7 @@ class PingPongDescriptorPublisher(object):
                         internal_vlr_count=num_internal_vlrs,
                         num_vnf_vms=2,
                         mano_ut=True,
-                        use_scale_group=True,
+                        use_scale_group=False,
                         use_mon_params=False,
                         )