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
)
--- /dev/null
+#
+# 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))
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)
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
})
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
)
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,
)
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
)
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,
)
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()
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:
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')
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):
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()
# 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):
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
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))
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
)
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)
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
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()
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.
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";
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 {
@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))
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:
if resp:
if 'error' in resp:
- details = resp['error']['Message']
+ details = resp['error']['message']
else:
exec_id = resp['action']['tag']
rc = resp['status']
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
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:
agent_nsr.id,
agent_vnfr.id,
prim,
- vnf_config)
+ vnf_config,
+ wait=True)
if rc == "failed":
msg = "Error executing initial config primitive" \
import abc
import os
import tempfile
+from urllib.parse import urlparse
import yaml
import gi
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':
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']
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
)
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
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)
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))
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
"""
"""
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 """
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
self._created = False
self.nsd = nsd
+ self._ssh_key = ssh_key
@property
def vnfd(self):
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
self._vnfrs = []
self._vdur_console_handler = {}
self._key_pairs = key_pairs
+ self._ssh_key = ssh_key
self._nsd_uuid = None
self._nsr_uuid = None
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
@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)
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
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
"""
self._http_api,
nsd_msg,
nsr_config_msg,
- key_pairs
+ key_pairs,
+ ssh_key,
)
self._openmano_nsrs[nsr_config_msg.id] = openmano_nsr
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})
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)
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
""" 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):
import yaml
import requests
import json
-
+from urllib.parse import urlparse
from collections import deque
from collections import defaultdict
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
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())
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
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 {}: {}".
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 = {}
""" 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
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):
"""
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
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())
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
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):
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)
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,
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)
# 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
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
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
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)
""" 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,
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 """
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,
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
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 """