New feature: Support for ping pong charm 01/901/1
authorPhilip Joseph <philip.joseph@riftio.com>
Thu, 5 Jan 2017 08:10:20 +0000 (08:10 +0000)
committerPhilip Joseph <philip.joseph@riftio.com>
Tue, 10 Jan 2017 11:28:48 +0000 (11:28 +0000)
Signed-off-by: Philip Joseph <philip.joseph@riftio.com>
15 files changed:
common/python/CMakeLists.txt
common/python/rift/mano/utils/ssh_keys.py [new file with mode: 0644]
examples/ping_pong_ns/rift/mano/examples/ping_pong_nsd.py
models/plugins/yang/nsr.yang
models/plugins/yang/vnfd.yang
models/plugins/yang/vnfr.yang
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_config.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/cloud.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/openmano_nsm.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsm_conman.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmplugin.py
rwlaunchpad/plugins/rwnsm/rift/tasklets/rwnsmtasklet/rwnsmtasklet.py
rwlaunchpad/plugins/rwvnfm/rift/tasklets/rwvnfmtasklet/rwvnfmtasklet.py

index 85ead68..46b30a1 100644 (file)
@@ -108,6 +108,7 @@ rift_python_install_tree(
     rift/mano/utils/__init.py__
     rift/mano/utils/compare_desc.py
     rift/mano/utils/juju_api.py
+    rift/mano/utils/ssh_keys.py
   COMPONENT ${PKG_LONG_NAME}
   PYTHON3_ONLY
   )
diff --git a/common/python/rift/mano/utils/ssh_keys.py b/common/python/rift/mano/utils/ssh_keys.py
new file mode 100644 (file)
index 0000000..6453f88
--- /dev/null
@@ -0,0 +1,147 @@
+#
+# 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.
+
+# Copyright 2016 RIFT.io Inc
+
+
+import argparse
+import logging
+import os
+import socket
+import stat
+import sys
+import tempfile
+
+from Crypto.PublicKey import RSA
+
+
+class ManoSshKey(object):
+    '''
+    Generate a SSH key pair and store them in a file
+    '''
+
+    def __init__(self, log, size=2048):
+        self._log = log
+        self._size = size
+
+        self._key = None
+        self._key_pem = None
+        self._pub_ssh = None
+        self._key_file = None
+        self._pub_file = None
+
+    @property
+    def log(self):
+        return self._log
+
+    @property
+    def size(self):
+        return self._size
+
+    @property
+    def private_key(self):
+        if self._key is None:
+            self._gen_keys()
+        return self._key_pem
+
+    @property
+    def public_key(self):
+        if self._key is None:
+            self._gen_keys()
+        return self._pub_ssh
+
+    @property
+    def private_key_file(self):
+        return self._key_file
+
+    @property
+    def public_key_file(self):
+        return self._pub_file
+
+    def _gen_keys(self):
+        if self._key:
+            return
+
+        self.log.info("Generating key of size: {}".format(self.size))
+
+        self._key = RSA.generate(self.size, os.urandom)
+        self._key_pem = self._key.exportKey('PEM').decode('utf-8')
+        self.log.debug("Private key PEM: {}".format(self._key_pem))
+
+        # Public key export as 'OpenSSH' has a bug
+        # (https://github.com/dlitz/pycrypto/issues/99)
+
+        username = None
+        try:
+            username = os.getlogin()
+            hostname = socket.getfqdn()
+        except OSError:
+            pass
+
+        pub = self._key.publickey().exportKey('OpenSSH').decode('utf-8')
+        if username:
+            self._pub_ssh = '{} {}@{}'.format(pub, username, hostname)
+        else:
+            self._pub_ssh = pub
+        self.log.debug("Public key SSH: {}".format(self._pub_ssh))
+
+    def write_to_disk(self,
+                      name="id_rsa",
+                      directory="."):
+        if self._key is None:
+            self._gen_keys()
+
+        path = os.path.abspath(directory)
+        self._pub_file = "{}/{}.pub".format(path, name)
+        self._key_file = "{}/{}.key".format(path, name)
+
+        with open(self._key_file, 'w') as content_file:
+            content_file.write(self.private_key)
+            os.chmod(self._key_file, stat.S_IREAD|stat.S_IWRITE)
+
+        with open(self._pub_file, 'w') as content_file:
+            content_file.write(self.public_key)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description='Generate SSH key pair')
+    parser.add_argument("-s", "--size", type=int, default=2048, help="Key size")
+    parser.add_argument("-d", "--directory", help="Directory to store the keys")
+    parser.add_argument("-n", "--name", help="Name for the key file")
+    parser.add_argument("--debug", help="Enable debug logging",
+                        action="store_true")
+    args = parser.parse_args()
+
+    fmt = logging.Formatter(
+        '%(asctime)-23s %(levelname)-5s  (%(name)s@%(process)d:' \
+        '%(filename)s:%(lineno)d) - %(message)s')
+    stderr_handler = logging.StreamHandler(stream=sys.stderr)
+    stderr_handler.setFormatter(fmt)
+    if args.debug:
+        logging.basicConfig(level=logging.DEBUG)
+    else:
+        logging.basicConfig(level=logging.INFO)
+    log = logging.getLogger('rw-mano-ssh-keys')
+    log.addHandler(stderr_handler)
+
+    log.info("Args passed: {}".format(args))
+    if args.directory:
+        path = args.directory
+    else:
+        path = tempfile.mkdtemp()
+
+    kp = ManoSshKey(log, size=args.size)
+    kp.write_to_disk(directory=path)
+    log.info("Private Key: {}".format(kp.private_key))
+    log.info("Public key: {}".format(kp.public_key))
+    log.info("Key file: {}, Public file: {}".format(kp.private_key_file,
+                                                    kp.public_key_file))
index eb6add4..8999507 100755 (executable)
@@ -118,6 +118,10 @@ class VirtualNetworkFunction(ManoDescriptor):
     def __init__(self, name, instance_count=1):
         self.vnfd_catalog = None
         self.vnfd = None
+        self.mano_ut = False
+        self.use_ns_init_conf = False
+        self.use_vca_conf = False
+        self.use_charm = False
         self.instance_count = instance_count
         self._placement_groups = []
         super(VirtualNetworkFunction, self).__init__(name)
@@ -125,7 +129,116 @@ class VirtualNetworkFunction(ManoDescriptor):
     def add_placement_group(self, group):
         self._placement_groups.append(group)
 
-    def add_vnf_access_point(self, mano_ut=False):
+    def add_vnf_conf_param_charm(self):
+        vnfd = self.descriptor.vnfd[0]
+        confparam = vnfd.config_parameter
+
+        src = confparam.create_config_parameter_source()
+        src.from_dict({
+            "name": "mgmt_ip",
+            "description": "Management IP address",
+            "attribute": "../../../mgmt-interface, ip-address",
+            "parameter" : [{
+                "config_primitive_name_ref": "config",
+                "config_primitive_parameter_ref": "ssh-hostname"
+            }]
+        })
+        confparam.config_parameter_source.append(src)
+
+        src = confparam.create_config_parameter_source()
+        src.from_dict({
+            "name": "username",
+            "description": "SSH username",
+            "value": "fedora",
+            "parameter" : [{
+                "config_primitive_name_ref": "config",
+                "config_primitive_parameter_ref": "ssh-username"
+            }]
+        })
+        confparam.config_parameter_source.append(src)
+
+        src = confparam.create_config_parameter_source()
+        src.from_dict({
+            "name": "ssh_key",
+            "description": "SSH private key file",
+            "attribute": "../../../mgmt-interface/ssh-key, private-key-file",
+            "parameter" : [{
+                "config_primitive_name_ref": "config",
+                "config_primitive_parameter_ref": "ssh-private-key"
+            }]
+        })
+        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": "set-server",
+                        "config_primitive_parameter_ref": "server-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": "set-server",
+                        "config_primitive_parameter_ref": "server-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": "set-server",
+                        "config_primitive_parameter_ref": "server-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": "set-server",
+                        "config_primitive_parameter_ref": "server-port"
+                    },
+                ]
+            })
+            confparam.config_parameter_request.append(req)
+
+    def add_vnf_conf_param(self):
         vnfd = self.descriptor.vnfd[0]
         confparam = vnfd.config_parameter
 
@@ -261,7 +374,7 @@ class VirtualNetworkFunction(ManoDescriptor):
             })
             confparam.config_parameter_request.append(req)
 
-    def add_ping_config(self, mano_ut=False, use_ns_init_conf=False):
+    def add_ping_config(self):
         vnfd = self.descriptor.vnfd[0]
         # Add vnf configuration
         vnf_config = vnfd.vnf_configuration
@@ -334,7 +447,7 @@ class VirtualNetworkFunction(ManoDescriptor):
         )
         vnf_config.initial_config_primitive.append(init_config)
 
-        if use_ns_init_conf is False:
+        if self.use_ns_init_conf is False:
             init_config = VnfdYang.InitialConfigPrimitive.from_dict(
                 {
                     "seq": 3,
@@ -343,7 +456,7 @@ class VirtualNetworkFunction(ManoDescriptor):
             )
             vnf_config.initial_config_primitive.append(init_config)
 
-    def add_pong_config(self, mano_ut=False, use_ns_init_conf=False):
+    def add_pong_config(self):
         vnfd = self.descriptor.vnfd[0]
         # Add vnf configuration
         vnf_config = vnfd.vnf_configuration
@@ -391,7 +504,7 @@ class VirtualNetworkFunction(ManoDescriptor):
         )
         vnf_config.initial_config_primitive.append(init_config)
 
-        if use_ns_init_conf is False:
+        if self.use_ns_init_conf is False:
             init_config = VnfdYang.InitialConfigPrimitive.from_dict(
                 {
                     "seq": 2,
@@ -400,10 +513,131 @@ class VirtualNetworkFunction(ManoDescriptor):
             )
             vnf_config.initial_config_primitive.append(init_config)
 
+    def add_charm_config(self):
+        vnfd = self.descriptor.vnfd[0]
+        # Add vnf configuration
+        vnf_config = vnfd.vnf_configuration
+
+        if 'pong_' in self.name:
+            mode = "pong"
+        else:
+            mode = "ping"
+
+        # Select "script" configuration
+        vnf_config.juju.charm = 'pingpong'
+
+        # Add config primitive
+        vnf_config.create_config_primitive()
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "start",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "stop",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "restart",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "config",
+            "parameter": [
+                {"name": "ssh-hostname", "data_type": "STRING"},
+                {"name": "ssh-username", "data_type": "STRING"},
+                {"name": "ssh-private-key", "data_type": "STRING"},
+                {"name": "mode", "data_type": "STRING",
+                 "default_value": "{}".format(mode),
+                 "read_only": "true"},
+            ],
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "set-server",
+            "parameter": [
+                {"name": "server-ip", "data_type": "STRING"},
+                {"name": "server-port", "data_type": "INTEGER"},
+            ],
+        })
+        vnf_config.config_primitive.append(prim)
+
+        if mode == 'ping':
+            prim = VnfdYang.ConfigPrimitive.from_dict({
+                "name": "set-rate",
+                "parameter": [
+                    {"name": "rate", "data_type": "INTEGER",
+                     "default_value": "5"},
+                ],
+            })
+            vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "start-traffic",
+        })
+        vnf_config.config_primitive.append(prim)
+
+        prim = VnfdYang.ConfigPrimitive.from_dict({
+            "name": "stop-traffic",
+        })
+        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": "start",
+            }
+        )
+        vnf_config.initial_config_primitive.append(init_config)
+
+        init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+            {
+                "seq": 3,
+                "config_primitive_ref": "set-server",
+            },
+        )
+        vnf_config.initial_config_primitive.append(init_config)
+
+        if mode == 'ping':
+            init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+                {
+                    "seq": 4,
+                    "config_primitive_ref": "set-rate",
+                },
+            )
+            vnf_config.initial_config_primitive.append(init_config)
+
+        if self.use_ns_init_conf is False:
+            init_config = VnfdYang.InitialConfigPrimitive.from_dict(
+                {
+                    "seq": 5,
+                    "config_primitive_ref": "start-traffic",
+                },
+            )
+            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, use_ns_init_conf=False,
-                use_vca_conf=False):
+                use_vca_conf=False, use_charm=False):
+        self.mano_ut = mano_ut
+        self.use_ns_init_conf = use_ns_init_conf
+        self.use_vca_conf = use_vca_conf
+        self.use_charm = use_charm
+
         self.descriptor = RwVnfdYang.YangData_Vnfd_VnfdCatalog()
         self.id = str(uuid.uuid1())
         vnfd = self.descriptor.vnfd.add()
@@ -471,22 +705,28 @@ class VirtualNetworkFunction(ManoDescriptor):
             mgmt_intf.dashboard_params.path = endpoint
             mgmt_intf.dashboard_params.port = mgmt_port
 
-            if cloud_init_file and len(cloud_init_file):
-                vdu.cloud_init_file = cloud_init_file
-            else:
-                vdu.cloud_init = cloud_init
-                if aws:
-                    vdu.cloud_init += "  - [ systemctl, restart, --no-block, elastic-network-interfaces.service ]\n"
+            if use_charm:
+                mgmt_intf.ssh_key = True
+
+            if not self.use_charm:
+                if cloud_init_file and len(cloud_init_file):
+                    vdu.cloud_init_file = cloud_init_file
+                else:
+                    vdu.cloud_init = cloud_init
+                    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)
+                if use_charm:
+                    self.add_vnf_conf_param_charm()
+                    self.add_charm_config()
                 else:
-                    self.add_ping_config(mano_ut=mano_ut,
-                                         use_ns_init_conf=use_ns_init_conf)
+                    self.add_vnf_conf_param()
+                    if 'pong_' in self.name:
+                        self.add_pong_config()
+                    else:
+                        self.add_ping_config()
 
             # sepcify the guest EPA
             if use_epa:
@@ -583,14 +823,14 @@ class VirtualNetworkFunction(ManoDescriptor):
                     member_vdu.member_vdu_ref = vdu.id
 
 
-    def write_to_file(self, outdir, output_format, use_vca_conf=False):
+    def write_to_file(self, outdir, output_format):
         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, use_vca_conf=use_vca_conf)
+        self.add_scripts(outdir)
 
     def add_cloud_init(self, outdir):
         script_dir = os.path.join(outdir, self.name, 'cloud_init')
@@ -610,9 +850,11 @@ 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:
+    def add_scripts(self, outdir):
+        if not self.use_charm:
+            self.add_cloud_init(outdir)
+
+        if self.use_vca_conf and not self.use_charm:
             self.add_vca_scripts(outdir)
 
     def add_vca_scripts(self, outdir):
@@ -927,7 +1169,7 @@ exit 0
                 self.nsd.monitoring_param.append(nsd_monp)
                 param_id += 1
 
-    def add_confparam_map(self):
+    def add_conf_param_map(self):
         nsd = self.nsd
 
         confparam_map = nsd.config_parameter_map.add()
@@ -1051,7 +1293,7 @@ exit 0
 
         # self.create_mon_params(vnfd_list)
         if use_vca_conf:
-            self.add_confparam_map()
+            self.add_conf_param_map()
 
     def write_config(self, outdir, vnfds):
 
@@ -1227,6 +1469,7 @@ def generate_ping_pong_descriptors(fmt="json",
                                    use_placement_group=True,
                                    use_ns_init_conf=True,
                                    use_vca_conf=True,
+                                   use_charm=False,
                                    ):
     # List of connection point groups
     # Each connection point group refers to a virtual link
@@ -1273,6 +1516,7 @@ def generate_ping_pong_descriptors(fmt="json",
             mano_ut=mano_ut,
             use_ns_init_conf=use_ns_init_conf,
             use_vca_conf=use_vca_conf,
+            use_charm=use_charm,
             )
 
     pong = VirtualNetworkFunction("pong_vnfd%s" % (suffix))
@@ -1315,6 +1559,7 @@ def generate_ping_pong_descriptors(fmt="json",
             mano_ut=mano_ut,
             use_ns_init_conf=use_ns_init_conf,
             use_vca_conf=use_vca_conf,
+            use_charm=use_charm,
             )
 
     # Initialize the member VNF index
@@ -1385,10 +1630,8 @@ def generate_ping_pong_descriptors(fmt="json",
     )
 
     if write_to_file:
-        ping.write_to_file(out_dir, ping_fmt if ping_fmt is not None else fmt,
-                           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)
+        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)
         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)
 
@@ -1407,6 +1650,8 @@ def main(argv=sys.argv[1:]):
     parser.add_argument('--pong-image-md5')
     parser.add_argument('--ping-cloud-init', default=None)
     parser.add_argument('--pong-cloud-init', default=None)
+    parser.add_argument('--charm', action="store_true", default=False)
+
     args = parser.parse_args()
     outdir = args.outdir
     output_format = args.format
@@ -1417,9 +1662,12 @@ def main(argv=sys.argv[1:]):
     use_pong_cloud_init_file = args.pong_cloud_init
 
     generate_ping_pong_descriptors(args.format, True, args.outdir, pingcount,
-                                   ping_md5sum=args.ping_image_md5, pong_md5sum=args.pong_image_md5,
+                                   ping_md5sum=args.ping_image_md5,
+                                   pong_md5sum=args.pong_image_md5,
                                    mano_ut=False,
-                                   use_scale_group=False,)
+                                   use_scale_group=False,
+                                   use_charm=args.charm,
+    )
 
 if __name__ == "__main__":
     main()
index e8b65ae..0ded043 100644 (file)
@@ -621,6 +621,18 @@ module nsr
         type uint32;
       }
 
+      container ssh-key-generated {
+        description "SSH key pair generated for this NS";
+        leaf public-key {
+          description "Public key generated";
+          type string;
+        }
+        leaf private-key-file {
+          description "Path to the private key file";
+          type string;
+        }
+      }
+
       list connection-point {
         description
             "List for external connection points.
index 0806bf1..e505804 100644 (file)
@@ -342,6 +342,13 @@ module vnfd
           type inet:port-number;
         }
 
+        leaf ssh-key {
+          description
+            "Whether SSH keys need to be generated and passed
+             to the RO and VCA during instantiation.";
+          type boolean;
+        }
+
         container dashboard-params {
           description "Parameters for the VNF dashboard";
 
index 69d5691..73ca152 100644 (file)
@@ -223,6 +223,18 @@ module vnfr
         leaf port {
           type inet:port-number;
         }
+
+        container ssh-key {
+          description "SSH key pair used for this VNF";
+          leaf public-key {
+            description "Public key configured on this VNF";
+            type string;
+          }
+          leaf private-key-file {
+            description "Path to the private key file";
+            type string;
+          }
+        }
       }
 
       list internal-vlr {
index 73a0005..016f712 100644 (file)
@@ -299,7 +299,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
 
     @asyncio.coroutine
     def _vnf_config_primitive(self, nsr_id, vnfr_id, primitive,
-                              vnf_config=None):
+                              vnf_config=None, wait=False):
         self._log.debug("jujuCA: VNF config primitive {} for nsr {}, "
                         "vnfr_id {}".
                         format(primitive, nsr_id, vnfr_id))
@@ -370,11 +370,10 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                             rc = yield from self.api.apply_config(
                                 params,
                                 service=service,
-                                wait=False)
+                                wait=True)
 
                             if rc:
-                                # Mark as pending and check later for the status
-                                rc = "pending"
+                                rc = "completed"
                                 self._log.debug("jujuCA: applied config {} "
                                                 "on {}".format(params, service))
                             else:
@@ -401,7 +400,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
 
                         if resp:
                             if 'error' in resp:
-                                details = resp['error']['Message']
+                                details = resp['error']['message']
                             else:
                                 exec_id = resp['action']['tag']
                                 rc = resp['status']
@@ -423,10 +422,17 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
 
         except KeyError as e:
             msg = "VNF %s does not have config primitives, e=%s", \
-                  vnfr_msg.name, e
-            self._log.error(msg)
+                  vnfr_id, e
+            self._log.exception(msg)
             raise ValueError(msg)
 
+        while wait and (rc in ['pending', 'running']):
+            self._log.debug("JujuCA: action {}, rc {}".
+                            format(exec_id, rc))
+            yield from asyncio.sleep(0.2, loop=self._loop)
+            status = yield from self.api.get_action_status(exec_id)
+            rc = status['status']
+
         return rc, exec_id, details
 
     @asyncio.coroutine
@@ -590,7 +596,7 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
 
             for primitive in primitives:
                 self._log.debug("(%s) Initial config primitive %s",
-                                vnfr['vnf_juju_name'], primitive)
+                                vnfr['vnf_juju_name'], primitive.as_dict())
                 if primitive.config_primitive_ref:
                     # Reference to a primitive in config primitive
                     class Primitive:
@@ -604,7 +610,8 @@ class JujuConfigPlugin(riftcm_config_plugin.RiftCMConfigPluginBase):
                         agent_nsr.id,
                         agent_vnfr.id,
                         prim,
-                        vnf_config)
+                        vnf_config,
+                        wait=True)
 
                     if rc == "failed":
                         msg = "Error executing initial config primitive" \
index 78f0aa0..e289cf4 100644 (file)
@@ -18,6 +18,7 @@ import asyncio
 import abc
 import os
 import tempfile
+from urllib.parse import urlparse
 import yaml
 
 import gi
@@ -325,6 +326,11 @@ class RiftCMConfigPluginBase(object):
 
     def convert_value(self, value, type_='STRING'):
         if type_ == 'STRING':
+            if value.startswith('file://'):
+                p = urlparse(value)
+                with open(p[2], 'r') as f:
+                    val = f.read()
+                    return(val)
             return str(value)
 
         if type_ == 'INTEGER':
index 6944d48..4ea516d 100644 (file)
@@ -468,11 +468,12 @@ class ConfigManagerConfig(object):
                 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))
+                                    format(vnfr['name'], vnfd.name))
                     return
             except AttributeError as e:
                 self._log.error("No config primitives found on VNFR {} ({})".
-                                format(vnfr.name, vnfd.name))
+                                format(vnfr['name'], vnfd.name))
+                continue
 
             cm_state = nsr_obj.find_vnfr_cm_state(vnfr['id'])
             srcs = cm_state['config_parameter']['config_parameter_source']
@@ -482,6 +483,9 @@ class ConfigManagerConfig(object):
             vnf_configuration['config_primitive'] = []
             for prim in prims:
                 confp = prim.as_dict()
+                if 'parameter' not in confp:
+                    continue
+
                 for param in confp['parameter']:
                     # First check the param in capabilities
                     found = False
@@ -1055,12 +1059,15 @@ class ConfigManagerConfig(object):
                         )
 
                 v['vdur'] = []
-                vdu_data = [(vdu['name'], vdu['management_ip'], vdu['vm_management_ip'], vdu['id'])
-                        for vdu in vnfr['vdur']]
+                vdu_data = []
+                for vdu in vnfr['vdur']:
+                    d = {}
+                    for k in ['name', 'management_ip', 'vm_management_ip', 'id']:
+                        if k in vdu:
+                            d[k] = vdu[k]
+                    vdu_data.append(d)
 
-                for data in vdu_data:
-                    data = dict(zip(['name', 'management_ip', 'vm_management_ip', 'id'] , data))
-                    v['vdur'].append(data)
+                v['vdur'].append(vdu_data)
 
                 inp['vnfr'][vnfr['member_vnf_index_ref']] = v
 
@@ -1152,7 +1159,7 @@ class ConfigManagerConfig(object):
                                   stderr=asyncio.subprocess.PIPE)
                     yield from process.wait()
                     if process.returncode:
-                        script_out, script_err = yield from proc.communicate()
+                        script_out, script_err = yield from process.communicate()
                         msg = "NSR {} initial config using {} failed with {}". \
                               format(nsr_name, script, process.returncode)
                         self._log.error(msg)
@@ -1163,7 +1170,7 @@ class ConfigManagerConfig(object):
                         os.remove(inp_file)
 
             except KeyError as e:
-                self._log.debug("Did not find initial config {}".
+                self._log.debug("Did not find initial config: {}".
                                 format(e))
 
 
index 32efff2..63c804c 100644 (file)
@@ -40,7 +40,7 @@ class RwNsPlugin(rwnsmplugin.NsmPluginBase):
         self._log = log
         self._loop = loop
 
-    def create_nsr(self, nsr_msg, nsd,key_pairs=None):
+    def create_nsr(self, nsr_msg, nsd, key_pairs=None, ssh_key=None):
         """
         Create Network service record
         """
@@ -92,6 +92,11 @@ class RwNsPlugin(rwnsmplugin.NsmPluginBase):
         """
         yield from vlr.terminate()
 
+    @asyncio.coroutine
+    def update_vnfr(self, vnfr):
+        """ Update the virtual network function record """
+        yield from vnfr.update_vnfm()
+
 
 class NsmPlugins(object):
     """ NSM Plugins """
index 6c18946..31b0544 100644 (file)
@@ -153,7 +153,7 @@ class VnfrConsoleOperdataDtsHandler(object):
 
 
 class OpenmanoVnfr(object):
-    def __init__(self, log, loop, cli_api, vnfr, nsd):
+    def __init__(self, log, loop, cli_api, vnfr, nsd, ssh_key=None):
         self._log = log
         self._loop = loop
         self._cli_api = cli_api
@@ -165,6 +165,7 @@ class OpenmanoVnfr(object):
         self._created = False
 
         self.nsd = nsd
+        self._ssh_key = ssh_key
 
     @property
     def vnfd(self):
@@ -260,7 +261,8 @@ class OpenmanoNSRecordState(Enum):
 class OpenmanoNsr(object):
     TIMEOUT_SECS = 300
 
-    def __init__(self, dts, log, loop, publisher, cli_api, http_api, nsd_msg, nsr_config_msg,key_pairs):
+    def __init__(self, dts, log, loop, publisher, cli_api, http_api, nsd_msg,
+                 nsr_config_msg, key_pairs, ssh_key):
         self._dts = dts
         self._log = log
         self._loop = loop
@@ -275,6 +277,7 @@ class OpenmanoNsr(object):
         self._vnfrs = []
         self._vdur_console_handler = {}
         self._key_pairs = key_pairs
+        self._ssh_key = ssh_key
 
         self._nsd_uuid = None
         self._nsr_uuid = None
@@ -320,6 +323,10 @@ class OpenmanoNsr(object):
             self._log.debug("Key pair  NSD  is %s",authorized_key)
             key_pairs.append(authorized_key.key)
 
+        if self._ssh_key:
+            self._log.debug("Pub key  NSD  is %s", self._ssh_key['public_key'])
+            key_pairs.append(self._ssh_key['public_key'])
+
         if key_pairs:
             cloud_config["key-pairs"] = key_pairs
 
@@ -434,7 +441,8 @@ class OpenmanoNsr(object):
 
     @asyncio.coroutine
     def add_vnfr(self, vnfr):
-        vnfr = OpenmanoVnfr(self._log, self._loop, self._cli_api, vnfr, nsd=self.nsd)
+        vnfr = OpenmanoVnfr(self._log, self._loop, self._cli_api, vnfr,
+                            nsd=self.nsd, ssh_key=self._ssh_key)
         yield from vnfr.create()
         self._vnfrs.append(vnfr)
 
@@ -635,8 +643,13 @@ class OpenmanoNsr(object):
                         self._log.debug("All VMs in VNF are active.  Marking as running.")
                         vnfr_msg.operational_status = "running"
 
-                        self._log.debug("Got VNF ip address: %s, mac-address: %s", vnf_ip_address, vnf_mac_address)
+                        self._log.debug("Got VNF ip address: %s, mac-address: %s",
+                                        vnf_ip_address, vnf_mac_address)
                         vnfr_msg.mgmt_interface.ip_address = vnf_ip_address
+                        vnfr_msg.mgmt_interface.ssh_key.public_key = \
+                                                    vnfr._ssh_key['public_key']
+                        vnfr_msg.mgmt_interface.ssh_key.private_key_file = \
+                                                    vnfr._ssh_key['private_key']
                         vnfr_msg.vnf_configuration.config_access.mgmt_ip_address = vnf_ip_address
 
 
@@ -815,7 +828,7 @@ class OpenmanoNsPlugin(rwnsmplugin.NsmPluginBase):
                 ro_account.openmano.tenant_id,
                 )
 
-    def create_nsr(self, nsr_config_msg, nsd_msg, key_pairs=None):
+    def create_nsr(self, nsr_config_msg, nsd_msg, key_pairs=None, ssh_key=None):
         """
         Create Network service record
         """
@@ -828,7 +841,8 @@ class OpenmanoNsPlugin(rwnsmplugin.NsmPluginBase):
                 self._http_api,
                 nsd_msg,
                 nsr_config_msg,
-                key_pairs
+                key_pairs,
+                ssh_key,
                 )
         self._openmano_nsrs[nsr_config_msg.id] = openmano_nsr
 
@@ -865,6 +879,12 @@ class OpenmanoNsPlugin(rwnsmplugin.NsmPluginBase):
         self._log.debug("Creating a task to update uptime for vnfr: %s", vnfr.id)
         self._vnfr_uptime_tasks[vnfr.id] = self._loop.create_task(self.vnfr_uptime_update(vnfr))
 
+    def update_vnfr(self, vnfr):
+        vnfr_msg = vnfr.vnfr_msg.deep_copy()
+        self._log.debug("Attempting to publish openmano vnf: %s", vnfr_msg)
+        with self._dts.transaction() as xact:
+            yield from self._publisher.publish_vnfr(xact, vnfr_msg)
+
     def vnfr_uptime_update(self, vnfr):
         try:
             vnfr_ = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict({'id': vnfr.id})
index 8073c4c..a237c3f 100644 (file)
@@ -101,7 +101,8 @@ class ROConfigManager(object):
 
                 yield from \
                     self.nsm.vnfrs[vnfrid].update_config_primitives(
-                        vnfr.vnf_configuration)
+                        vnfr.vnf_configuration,
+                        self.nsm.nsrs[nsrid])
 
             # Update the NSR's config status
             new_status = ROConfigManager.map_config_status(cm_nsr.state)
index ec16259..3febfd1 100755 (executable)
@@ -48,7 +48,7 @@ class NsmPluginBase(object):
     def nsm(self):
         return self._nsm
 
-    def create_nsr(self, nsr):
+    def create_nsr(self, nsr, nsd, key_pairs=None, ssh_key=None):
         """ Create an NSR """
         pass
 
@@ -75,6 +75,12 @@ class NsmPluginBase(object):
         """ Instantiate the virtual link"""
         pass
 
+    @abc.abstractmethod
+    @asyncio.coroutine
+    def update_vnfr(self, vnfr):
+        """ Update the virtual network function record """
+        pass
+
     @abc.abstractmethod
     @asyncio.coroutine
     def get_nsr(self, nsr_path):
index dce9ebe..c9f7bb9 100755 (executable)
@@ -27,7 +27,7 @@ import uuid
 import yaml
 import requests
 import json
-
+from urllib.parse import urlparse
 
 from collections import deque
 from collections import defaultdict
@@ -59,10 +59,11 @@ from gi.repository import (
     ProtobufC,
 )
 
-import rift.tasklets
+from rift.mano.utils.ssh_keys import ManoSshKey
 import rift.mano.ncclient
 import rift.mano.config_data.config
 import rift.mano.dts as mano_dts
+import rift.tasklets
 
 from . import rwnsm_conman as conman
 from . import cloud
@@ -963,8 +964,9 @@ class VirtualNetworkFunctionRecord(object):
 
         vnfr = RwVnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr.from_dict(vnfr_dict)
 
-        vnfr.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict(self.vnfd.as_dict(),
-                                                                          ignore_missing_keys=True)
+        vnfr.vnfd = VnfrYang.YangData_Vnfr_VnfrCatalog_Vnfr_Vnfd.from_dict(
+            self.vnfd.as_dict(),
+            ignore_missing_keys=True)
         vnfr.member_vnf_index_ref = self.member_vnf_index
         vnfr.vnf_configuration.from_dict(self._vnfd.vnf_configuration.as_dict())
 
@@ -1049,7 +1051,7 @@ class VirtualNetworkFunctionRecord(object):
         return False
 
     @asyncio.coroutine
-    def update_config_primitives(self, vnf_config):
+    def update_config_primitives(self, vnf_config, nsr):
         # Update only after we are configured
         if self._config_status == NsrYang.ConfigStates.INIT:
             return
@@ -1084,7 +1086,7 @@ class VirtualNetworkFunctionRecord(object):
             self._vnfr_msg = self.create_vnfr_msg()
 
             try:
-                yield from self.update_vnfm()
+                yield from nsr.nsm_plugin.update_vnfr(self)
             except Exception as e:
                 self._log.error("Exception updating VNFM with new config "
                                 "primitive for VNFR {}: {}".
@@ -1296,6 +1298,8 @@ class NetworkServiceRecord(object):
         self._nsr_msg = None
         self._nsr_regh = None
         self._key_pairs = key_pairs
+        self._ssh_key_file = None
+        self._ssh_pub_key = None
         self._vlrs = []
         self._vnfrs = {}
         self._vnfds = {}
@@ -1431,6 +1435,14 @@ class NetworkServiceRecord(object):
         """ Config status for NSR """
         return self._config_status
 
+    @property
+    def public_key(self):
+        return self._ssh_pub_key
+
+    @property
+    def private_key(self):
+        return self._ssh_key_file
+
     def resolve_placement_group_cloud_construct(self, input_group):
         """
         Returns the cloud specific construct for placement group
@@ -1495,6 +1507,33 @@ class NetworkServiceRecord(object):
             self._log.exception(e)
             return "Unknown trigger"
 
+    @asyncio.coroutine
+    def generate_ssh_key_pair(self, config_xact):
+        '''Generate a ssh key pair if required'''
+        if self._ssh_key_file:
+            self._log.debug("Key pair already generated")
+            return
+
+        gen_key = False
+        for cv in self.nsd_msg.constituent_vnfd:
+            vnfd = self._get_vnfd(cv.vnfd_id_ref, config_xact)
+            if vnfd and vnfd.mgmt_interface.ssh_key:
+                gen_key = True
+                break
+
+        if not gen_key:
+            return
+
+        try:
+            key = ManoSshKey(self._log)
+            path = tempfile.mkdtemp()
+            key.write_to_disk(name=self.id, directory=path)
+            self._ssh_key_file = "file://{}".format(key.private_key_file)
+            self._ssh_pub_key = key.public_key
+        except Exception as e:
+            self._log.exception("Error generating ssh key for {}: {}".
+                                format(self.nsr_cfg_msg.name, e))
+
     @asyncio.coroutine
     def instantiate_vls(self):
         """
@@ -2498,6 +2537,15 @@ class NetworkServiceRecord(object):
 
         yield from self.nsm_plugin.terminate_ns(self)
 
+        # Remove the generated SSH key
+        if self._ssh_key_file:
+            p = urlparse(self._ssh_key_file)
+            if p[0] == 'file':
+                path = os.path.dirname(p[2])
+                self._log.debug("NSR {}: Removing keys in {}".format(self.name,
+                                                                     path))
+                shutil.rmtree(path, ignore_errors=True)
+
         # Move the state to TERMINATED
         self.set_state(NetworkServiceRecordState.TERMINATED)
         event_descr = "Terminated NS Id:%s" % self.id
@@ -2544,6 +2592,11 @@ class NetworkServiceRecord(object):
         nsr.create_time = self._create_time
         nsr.uptime = int(time.time()) - self._create_time
 
+        # Generated SSH key
+        if self._ssh_pub_key:
+            nsr.ssh_key_generated.private_key_file = self._ssh_key_file
+            nsr.ssh_key_generated.public_key = self._ssh_pub_key
+
         for cfg_prim in self.nsd_msg.service_primitive:
             cfg_prim = NsrYang.YangData_Nsr_NsInstanceOpdata_Nsr_ServicePrimitive.from_dict(
                     cfg_prim.as_dict())
@@ -3283,6 +3336,7 @@ class NsrDtsHandler(object):
             self._log.debug("Got nsr apply (xact: %s) (action: %s)(scr: %s)",
                             xact, action, scratch)
 
+            @asyncio.coroutine
             def handle_create_nsr(msg, key_pairs=None, restart_mode=False):
                 # Handle create nsr requests """
                 # Do some validations
@@ -3293,7 +3347,10 @@ class NsrDtsHandler(object):
 
                 self._log.debug("Creating NetworkServiceRecord %s  from nsr config  %s",
                                msg.id, msg.as_dict())
-                nsr = self.nsm.create_nsr(msg, key_pairs=key_pairs, restart_mode=restart_mode)
+                nsr = self.nsm.create_nsr(msg,
+                                          xact,
+                                          key_pairs=key_pairs,
+                                          restart_mode=restart_mode)
                 return nsr
 
             def handle_delete_nsr(msg):
@@ -3320,6 +3377,12 @@ class NsrDtsHandler(object):
                 self._log.info("Beginning NS instantiation: %s", nsr.id)
                 yield from self._nsm.instantiate_ns(nsr.id, xact)
 
+            @asyncio.coroutine
+            def instantiate_ns(msg, key_pairs, restart_mode=False):
+                nsr = yield from handle_create_nsr(msg, key_pairs,
+                                                   restart_mode=restart_mode)
+                yield from begin_instantiation(nsr)
+
             self._log.debug("Got nsr apply (xact: %s) (action: %s)(scr: %s)",
                             xact, action, scratch)
 
@@ -3328,9 +3391,8 @@ class NsrDtsHandler(object):
                 for element in self._key_pair_regh.elements:
                     key_pairs.append(element)
                 for element in self._nsr_regh.elements:
-                    nsr = handle_create_nsr(element, key_pairs, restart_mode=True)
-                    self._loop.create_task(begin_instantiation(nsr))
-
+                    self._loop.create_task(instantiate_ns(element, key_pairs,
+                                                          restart_mode=True))
 
             (added_msgs, deleted_msgs, updated_msgs) = get_add_delete_update_cfgs(self._nsr_regh,
                                                                                   xact,
@@ -3343,8 +3405,7 @@ class NsrDtsHandler(object):
                 if msg.id not in self._nsm.nsrs:
                     self._log.info("Create NSR received in on_apply to instantiate NS:%s", msg.id)
                     key_pairs = get_nsr_key_pairs(self._key_pair_regh, xact)
-                    nsr = handle_create_nsr(msg,key_pairs)
-                    self._loop.create_task(begin_instantiation(nsr))
+                    self._loop.create_task(instantiate_ns(msg, key_pairs))
 
             for msg in deleted_msgs:
                 self._log.info("Delete NSR received in on_apply to terminate NS:%s", msg.id)
@@ -3884,7 +3945,7 @@ class NsManager(object):
         # Not calling in a separate task as this is called from a separate task
         yield from nsr.delete_vl_instance(vld)
 
-    def create_nsr(self, nsr_msg, key_pairs=None,restart_mode=False):
+    def create_nsr(self, nsr_msg, config_xact, key_pairs=None, restart_mode=False):
         """ Create an NSR instance """
         if nsr_msg.id in self._nsrs:
             msg = "NSR id %s already exists" % nsr_msg.id
@@ -3910,7 +3971,18 @@ class NsManager(object):
                                    vlr_handler=self._ro_plugin_selector._records_publisher._vlr_pub_hdlr
                                    )
         self._nsrs[nsr_msg.id] = nsr
-        nsm_plugin.create_nsr(nsr_msg, nsr_msg.nsd, key_pairs)
+
+        # Generate ssh key pair if required
+        yield from nsr.generate_ssh_key_pair(config_xact)
+
+        self._log.debug("NSR {}: SSh key generated: {}".format(nsr_msg.name,
+                                                               nsr.public_key))
+
+        ssh_key = {'private_key': nsr.private_key,
+                   'public_key': nsr.public_key
+        }
+
+        nsm_plugin.create_nsr(nsr_msg, nsr_msg.nsd, key_pairs, ssh_key=ssh_key)
 
         return nsr
 
index f456b16..2958cd9 100755 (executable)
@@ -502,7 +502,20 @@ class VirtualDeploymentUnitRecord(object):
     def vdud_cloud_init(self):
         """ Return the cloud-init contents for the VDU """
         if self._vdud_cloud_init is None:
-            self._vdud_cloud_init = self.cloud_init()
+            ci = self.cloud_init()
+
+            # VNFR ssh public key, if available
+            if self._vnfr.public_key:
+                if not ci:
+                    ci = "#cloud-config"
+                self._vdud_cloud_init = """{}
+ssh_authorized_keys:
+  - {}""". \
+                  format(ci, self._vnfr.public_key)
+            else:
+                self._vdud_cloud_init = ci
+
+            self._log.debug("Cloud init: {}".format(self._vdud_cloud_init))
 
         return self._vdud_cloud_init
 
@@ -1159,6 +1172,9 @@ class VirtualNetworkFunctionRecord(object):
         self._rw_vnfd = None
         self._vnfd_ref_count = 0
 
+        self._ssh_pub_key = None
+        self._ssh_key_file = None
+
     def _get_vdur_from_vdu_id(self, vdu_id):
         self._log.debug("Finding vdur for vdu_id %s", vdu_id)
         self._log.debug("Searching through vdus: %s", self._vdus)
@@ -1254,6 +1270,10 @@ class VirtualNetworkFunctionRecord(object):
         """ Config agent status for this VNFR """
         return self._config_status
 
+    @property
+    def public_key(self):
+        return self._ssh_pub_key
+
     def component_by_name(self, component_name):
         """ Find a component by name in the inventory list"""
         mangled_name = VcsComponent.mangle_name(component_name,
@@ -1278,6 +1298,22 @@ class VirtualNetworkFunctionRecord(object):
                     return nsr
         return None
 
+    @asyncio.coroutine
+    def get_nsr_opdata(self):
+        """ NSR opdata associated with this VNFR """
+        xpath = "D,/nsr:ns-instance-opdata/nsr:nsr" \
+            "[nsr:ns-instance-config-ref = '{}']". \
+            format(self._vnfr_msg.nsr_id_ref)
+
+        results = yield from self._dts.query_read(xpath, rwdts.XactFlag.MERGE)
+
+        for result in results:
+            entry = yield from result
+            nsr_op = entry.result
+            return nsr_op
+
+        return None
+
     @asyncio.coroutine
     def start_component(self, component_name, ip_addr):
         """ Start a component in the VNFR by name """
@@ -1325,6 +1361,10 @@ class VirtualNetworkFunctionRecord(object):
         if port is not None:
             mgmt_intf.port = port
 
+        if self._ssh_pub_key:
+            mgmt_intf.ssh_key.public_key = self._ssh_pub_key
+            mgmt_intf.ssh_key.private_key_file = self._ssh_key_file
+
         vnfr_dict = {"id": self._vnfr_id,
                      "nsr_id_ref": self._vnfr_msg.nsr_id_ref,
                      "name": self.name,
@@ -1388,7 +1428,7 @@ class VirtualNetworkFunctionRecord(object):
         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()))
+                        format(self._vnfr.as_dict()))
         yield from self.publish(xact)
 
     @property
@@ -1803,6 +1843,11 @@ class VirtualNetworkFunctionRecord(object):
         self.set_state(VirtualNetworkFunctionRecordState.VL_INIT_PHASE)
         self._rw_vnfd = yield from self._vnfm.fetch_vnfd(self._vnfd_id)
 
+        nsr_op = yield from self.get_nsr_opdata()
+        if nsr_op:
+            self._ssh_key_file = nsr_op.ssh_key_generated.private_key_file
+            self._ssh_pub_key = nsr_op.ssh_key_generated.public_key
+
         @asyncio.coroutine
         def fetch_vlrs():
             """ Fetch VLRs """