DBNAME="mano_db"
QUIET_MODE=""
#TODO update it with the last database version
-LAST_DB_VERSION=26
+LAST_DB_VERSION=27
# Detect paths
MYSQL=$(which mysql)
#[ $OPENMANO_VER_NUM -ge 5021 ] && DB_VERSION=24 #0.5.21 => 24
#[ $OPENMANO_VER_NUM -ge 5022 ] && DB_VERSION=25 #0.5.22 => 25
#[ $OPENMANO_VER_NUM -ge 5024 ] && DB_VERSION=26 #0.5.24 => 26
+#[ $OPENMANO_VER_NUM -ge 5025 ] && DB_VERSION=27 #0.5.25 => 27
#TODO ... put next versions here
function upgrade_to_1(){
function upgrade_to_24(){
# echo " upgrade database from version 0.23 to version 0.24"
echo " Add 'count' to table 'vms'"
+
sql "ALTER TABLE vms ADD COLUMN count SMALLINT NOT NULL DEFAULT '1' AFTER vnf_id;"
sql "INSERT INTO schema_version (version_int, version, openmano_ver, comments, date) "\
"VALUES (24, '0.24', '0.5.21', 'Added vnfd fields', '2017-08-29');"
sql "DELETE FROM schema_version WHERE version_int='26';"
}
+function upgrade_to_27(){
+ # echo " upgrade database from version 0.26 to version 0.27"
+ echo " Added 'encrypted_RO_priv_key','RO_pub_key' to table 'nfvo_tenants'"
+ sql "ALTER TABLE nfvo_tenants ADD COLUMN encrypted_RO_priv_key VARCHAR(2000) NULL AFTER description;"
+ sql "ALTER TABLE nfvo_tenants ADD COLUMN RO_pub_key VARCHAR(510) NULL AFTER encrypted_RO_priv_key;"
+
+ sql "INSERT INTO schema_version (version_int, version, openmano_ver, comments, date) "\
+ "VALUES (27, '0.27', '0.5.25', 'Added encrypted_RO_priv_key,RO_pub_key to table nfvo_tenants', '2017-09-29');"
+}
+function downgrade_from_26(){
+ # echo " downgrade database from version 0.27 to version 0.26"
+ echo " Remove 'encrypted_RO_priv_key','RO_pub_key' to table 'nfvo_tenants'"
+ sql "ALTER TABLE nfvo_tenants DROP COLUMN encrypted_RO_priv_key;"
+ sql "ALTER TABLE nfvo_tenants DROP COLUMN RO_pub_key;"
+ sql "DELETE FROM schema_version WHERE version_int='27';"
+}
function upgrade_to_X(){
echo " change 'datacenter_nets'"
sql "ALTER TABLE datacenter_nets ADD COLUMN vim_tenant_id VARCHAR(36) NOT NULL AFTER datacenter_id, DROP INDEX name_datacenter_id, ADD UNIQUE INDEX name_datacenter_id (name, datacenter_id, vim_tenant_id);"
instance_scenario_action_parser = subparsers.add_parser('instance-scenario-action', parents=[parent_parser], help="invoke an action over part or the whole scenario instance")
instance_scenario_action_parser.add_argument("name", action="store", help="name or uuid of the scenario instance")
instance_scenario_action_parser.add_argument("action", action="store", type=str, \
- choices=["start","pause","resume","shutoff","shutdown","forceOff","rebuild","reboot", "console"],\
+ choices=["start","pause","resume","shutoff","shutdown","forceOff","rebuild","reboot", "console", "add_public_key"],\
help="action to send")
instance_scenario_action_parser.add_argument("param", nargs='?', help="addional param of the action. e.g. console type (novnc, ...), reboot type (TODO)")
instance_scenario_action_parser.add_argument("--vnf", action="append", help="VNF to act on (can use several entries)")
__author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes"
__date__ = "$26-aug-2014 11:09:29$"
-__version__ = "0.5.24-r534"
+__version__ = "0.5.25-r535"
version_date = "Sep 2017"
-database_version = 26 # expected database schema version
+database_version = 27 # expected database schema version
+
global global_config
global logger
import time as t
from lib_osm_openvim import ovim as ovim_module
from lib_osm_openvim.ovim import ovimException
+from Crypto.PublicKey import RSA
import osm_im.vnfd as vnfd_catalog
import osm_im.nsd as nsd_catalog
-
from pyangbind.lib.serialise import pybindJSONDecoder
from itertools import chain
# cloud-init
boot_data = {}
if vdu.get("cloud-init"):
- boot_data["user-data"] = vdu["cloud-init"]
+ boot_data["user-data"] = str(vdu["cloud-init"])
elif vdu.get("cloud-init-file"):
# TODO Where this file content is present???
# boot_data["user-data"] = rift_vnfd.files[vdu["cloud-init-file"]]
mgmt_access["interface_id"] = cp_name2iface_uuid[vnfd["mgmt-interface"]["cp"]]
default_user = get_str(vnfd.get("vnf-configuration", {}).get("config-access", {}).get("ssh-access", {}),
"default-user", 64)
+
if default_user:
mgmt_access["default_user"] = default_user
+ required = get_str(vnfd.get("vnf-configuration", {}).get("config-access", {}).get("ssh-access", {}),
+ "required", 6)
+ if required:
+ mgmt_access["required"] = required
+
if mgmt_access:
db_vnf["mgmt_access"] = yaml.safe_dump(mgmt_access, default_flow_style=True, width=256)
+
+
db_vnfs.append(db_vnf)
db_tables=[
{"vnfs": db_vnfs},
#logger.error("start_scenario %s", error_text)
raise NfvoException(error_text, e.http_code)
-
def unify_cloud_config(cloud_config_preserve, cloud_config):
""" join the cloud config information into cloud_config_preserve.
In case of conflict cloud_config_preserve preserves
d[k] = u[k]
return d
-
def create_instance(mydb, tenant_id, instance_dict):
# print "Checking that nfvo_tenant_id exists and getting the VIM URI and the VIM tenant_id"
# logger.debug("Creating instance...")
default_datacenter_id, vim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
myvims[default_datacenter_id] = vim
myvim_threads_id[default_datacenter_id], _ = get_vim_thread(mydb, tenant_id, default_datacenter_id)
+ tenant = mydb.get_rows_by_id('nfvo_tenants', tenant_id)
# myvim_tenant = myvim['tenant_id']
+
rollbackList=[]
# print "Checking that the scenario exists and getting the scenario dictionary"
# 0.1 parse cloud-config parameters
cloud_config = unify_cloud_config(instance_dict.get("cloud-config"), scenarioDict.get("cloud-config"))
+ # We add the RO key to cloud_config
+ if tenant[0].get('RO_pub_key'):
+ RO_key = {"key-pairs": [tenant[0]['RO_pub_key']]}
+ cloud_config = unify_cloud_config(cloud_config, RO_key)
# 0.2 merge instance information into scenario
# Ideally, the operation should be as simple as: update(scenarioDict,instance_dict)
return 0, 'Scenario instance ' + instance_id + ' refreshed.'
-
def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
#print "Checking that the instance_id exists and getting the instance dictionary"
instanceDict = mydb.get_instance_scenario(instance_id, nfvo_tenant)
vm['uuid'] not in input_vms and vm['name'] not in input_vms:
continue
try:
- data = myvim.action_vminstance(vm['vim_vm_id'], action_dict)
- if "console" in action_dict:
- if not global_config["http_console_proxy"]:
- vm_result[ vm['uuid'] ] = {"vim_result": 200,
- "description": "{protocol}//{ip}:{port}/{suffix}".format(
- protocol=data["protocol"],
- ip = data["server"],
- port = data["port"],
- suffix = data["suffix"]),
- "name":vm['name']
- }
- vm_ok +=1
- elif data["server"]=="127.0.0.1" or data["server"]=="localhost":
- vm_result[ vm['uuid'] ] = {"vim_result": -HTTP_Unauthorized,
- "description": "this console is only reachable by local interface",
- "name":vm['name']
- }
- vm_error+=1
- else:
- #print "console data", data
+ if "add_public_key" in action_dict:
+ mgmt_access = {}
+ if sce_vnf.get('mgmt_access'):
+ mgmt_access = yaml.load(sce_vnf['mgmt_access'])
+ ssh_access = mgmt_access['config-access']['ssh-access']
+ tenant = mydb.get_rows_by_id('nfvo_tenants', nfvo_tenant)
try:
- console_thread = create_or_use_console_proxy_thread(data["server"], data["port"])
+ if ssh_access['required'] and ssh_access['default-user']:
+ if 'ip_address' in vm:
+ mgmt_ip = vm['ip_address'].split(';')
+ password = mgmt_access['config-access'].get('password')
+ priv_RO_key = decrypt_key(tenant[0]['encrypted_RO_priv_key'], tenant[0]['uuid'])
+ myvim.inject_user_key(mgmt_ip[0], ssh_access['default-user'],
+ action_dict['add_public_key'],
+ password=password, ro_key=priv_RO_key)
+ else:
+ raise NfvoException("Unable to inject ssh key in vm: {} - Aborting".format(vm['uuid']),
+ HTTP_Internal_Server_Error)
+ except KeyError:
+ raise NfvoException("Unable to inject ssh key in vm: {} - Aborting".format(vm['uuid']),
+ HTTP_Internal_Server_Error)
+ else:
+ raise NfvoException("Unable to inject ssh key in vm: {} - Aborting".format(vm['uuid']),
+ HTTP_Internal_Server_Error)
+ else:
+ data = myvim.action_vminstance(vm['vim_vm_id'], action_dict)
+ if "console" in action_dict:
+ if not global_config["http_console_proxy"]:
vm_result[ vm['uuid'] ] = {"vim_result": 200,
"description": "{protocol}//{ip}:{port}/{suffix}".format(
protocol=data["protocol"],
- ip = global_config["http_console_host"],
- port = console_thread.port,
+ ip = data["server"],
+ port = data["port"],
suffix = data["suffix"]),
"name":vm['name']
}
vm_ok +=1
- except NfvoException as e:
- vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
+ elif data["server"]=="127.0.0.1" or data["server"]=="localhost":
+ vm_result[ vm['uuid'] ] = {"vim_result": -HTTP_Unauthorized,
+ "description": "this console is only reachable by local interface",
+ "name":vm['name']
+ }
vm_error+=1
+ else:
+ #print "console data", data
+ try:
+ console_thread = create_or_use_console_proxy_thread(data["server"], data["port"])
+ vm_result[ vm['uuid'] ] = {"vim_result": 200,
+ "description": "{protocol}//{ip}:{port}/{suffix}".format(
+ protocol=data["protocol"],
+ ip = global_config["http_console_host"],
+ port = console_thread.port,
+ suffix = data["suffix"]),
+ "name":vm['name']
+ }
+ vm_ok +=1
+ except NfvoException as e:
+ vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
+ vm_error+=1
- else:
- vm_result[ vm['uuid'] ] = {"vim_result": 200, "description": "ok", "name":vm['name']}
- vm_ok +=1
+ else:
+ vm_result[ vm['uuid'] ] = {"vim_result": 200, "description": "ok", "name":vm['name']}
+ vm_ok +=1
except vimconn.vimconnException as e:
vm_result[ vm['uuid'] ] = {"vim_result": e.http_code, "name":vm['name'], "description": str(e)}
vm_error+=1
raise NfvoException("tenant '{}' not found".format(tenant_id), HTTP_Not_Found)
return
-
def new_tenant(mydb, tenant_dict):
- tenant_id = mydb.new_row("nfvo_tenants", tenant_dict, add_uuid=True)
- return tenant_id
+ tenant_uuid = str(uuid4())
+ tenant_dict['uuid'] = tenant_uuid
+ try:
+ pub_key, priv_key = create_RO_keypair(tenant_uuid)
+ tenant_dict['RO_pub_key'] = pub_key
+ tenant_dict['encrypted_RO_priv_key'] = priv_key
+ mydb.new_row("nfvo_tenants", tenant_dict)
+ except db_base_Exception as e:
+ raise NfvoException("Error creating the new tenant: {} ".format(tenant_dict['name']) + str(e), HTTP_Internal_Server_Error)
+ return tenant_uuid
def delete_tenant(mydb, tenant):
#get nfvo_tenant info
def datacenter_sdn_port_mapping_delete(mydb, tenant_id, datacenter_id):
return ovim.clear_of_port_mapping(db_filter={"region":datacenter_id})
+
+def create_RO_keypair(tenant_id):
+ """
+ Creates a public / private keys for a RO tenant and returns their values
+ Params:
+ tenant_id: ID of the tenant
+ Return:
+ public_key: Public key for the RO tenant
+ private_key: Encrypted private key for RO tenant
+ """
+
+ bits = 2048
+ key = RSA.generate(bits)
+ try:
+ public_key = key.publickey().exportKey('OpenSSH')
+ if isinstance(public_key, ValueError):
+ raise NfvoException("Unable to create public key: {}".format(public_key), HTTP_Internal_Server_Error)
+ private_key = key.exportKey(passphrase=tenant_id, pkcs=8)
+ except (ValueError, NameError) as e:
+ raise NfvoException("Unable to create private key: {}".format(e), HTTP_Internal_Server_Error)
+ return public_key, private_key
+
+def decrypt_key (key, tenant_id):
+ """
+ Decrypts an encrypted RSA key
+ Params:
+ key: Private key to be decrypted
+ tenant_id: ID of the tenant
+ Return:
+ unencrypted_key: Unencrypted private key for RO tenant
+ """
+ try:
+ key = RSA.importKey(key,tenant_id)
+ unencrypted_key = key.exportKey('PEM')
+ if isinstance(unencrypted_key, ValueError):
+ raise NfvoException("Unable to decrypt the private key: {}".format(unencrypted_key), HTTP_Internal_Server_Error)
+ except ValueError as e:
+ raise NfvoException("Unable to decrypt the private key: {}".format(e), HTTP_Internal_Server_Error)
+ return unencrypted_key
myVNFDict["description"] = vnf_descriptor['vnf']['description']
myVNFDict["class"] = vnf_descriptor['vnf'].get('class',"MISC")
myVNFDict["tenant_id"] = vnf_descriptor['vnf'].get("tenant_id")
-
+
vnf_id = self._new_row_internal('vnfs', myVNFDict, add_uuid=True, root_uuid=None, created_time=created_time)
#print "Adding new vms to the NFVO database"
#For each vm, we must create the appropriate vm in the NFVO database.
self.logger.debug(cmd)
self.cur.execute(cmd)
scenario_dict['vnfs'] = self.cur.fetchall()
+
for vnf in scenario_dict['vnfs']:
+ cmd = "SELECT mgmt_access FROM vnfs WHERE uuid='{}'".format(scenario_dict['vnfs'][0]['vnf_id'])
+ self.logger.debug(cmd)
+ self.cur.execute(cmd)
+ mgmt_access_dict = self.cur.fetchall()
+ if mgmt_access_dict[0].get('mgmt_access'):
+ vnf['mgmt_access'] = yaml.load(mgmt_access_dict[0]['mgmt_access'])
+ else:
+ vnf['mgmt_access'] = None
#sce_interfaces
cmd = "SELECT scei.uuid,scei.sce_net_id,scei.interface_id,i.external_name,scei.ip_address"\
" FROM sce_interfaces as scei join interfaces as i on scei.interface_id=i.uuid"\
cmd = "DELETE FROM scenarios WHERE uuid='{}'".format(scenario_uuid)
self.logger.debug(cmd)
self.cur.execute(cmd)
-
+
return scenario_uuid + " " + scenario_name
except (mdb.Error, AttributeError) as e:
self._format_error(e, tries, "delete", "instances running")
instance_dict["cloud-config"] = yaml.load(instance_dict["cloud_config"])
del instance_dict["cloud_config"]
- # instance_vnfs
- cmd = "SELECT iv.uuid as uuid,sv.vnf_id as vnf_id,sv.name as vnf_name, sce_vnf_id, datacenter_id, datacenter_tenant_id"\
- " FROM instance_vnfs as iv join sce_vnfs as sv on iv.sce_vnf_id=sv.uuid" \
+ #instance_vnfs
+ cmd = "SELECT iv.uuid as uuid,sv.vnf_id as vnf_id,sv.name as vnf_name, sce_vnf_id, datacenter_id, datacenter_tenant_id, v.mgmt_access"\
+ " FROM instance_vnfs as iv join sce_vnfs as sv on iv.sce_vnf_id=sv.uuid join vnfs as v on iv.vnf_id=v.uuid" \
" WHERE iv.instance_scenario_id='{}'" \
" ORDER BY iv.created_at ".format(instance_dict['uuid'])
self.logger.debug(cmd)
for vnf in instance_dict['vnfs']:
vnf_manage_iface_list=[]
#instance vms
- cmd = "SELECT iv.uuid as uuid, vim_vm_id, status, error_msg, vim_info, iv.created_at as created_at, name "\
+ cmd = "SELECT iv.uuid as uuid, vim_vm_id, status, error_msg, vim_info, iv.created_at as created_at, name"\
" FROM instance_vms as iv join vms on iv.vm_id=vms.uuid "\
" WHERE instance_vnf_id='{}' ORDER BY iv.created_at".format(vnf['uuid'])
self.logger.debug(cmd)
"availability_zone": name_schema,
"VNFC image": {"oneOf": [path_schema, http_schema]},
"image checksum": checksum_schema,
- "image metadata": metadata_schema,
+ "image metadata": metadata_schema,
#"cloud-config": cloud_config_schema, #common for all vnfs in the scenario
"processor": {
"type":"object",
"class": nameshort_schema,
"public": {"type" : "boolean"},
"physical": {"type" : "boolean"},
+ "default_user": name_schema,
"tenant_id": id_schema, #only valid for admin
"external-connections": {"type" : "array", "items": external_connection_schema, "minItems":1},
"internal-connections": {"type" : "array", "items": internal_connection_schema, "minItems":1},
"additionalProperties": False
}
-#VNFD schema for OSM R1
+#VNFD schema for OSM R1
vnfd_schema_v02 = {
"title":"vnfd information schema v0.2",
"$schema": "http://json-schema.org/draft-04/schema#",
"reboot":{
"type": ["object","null"],
},
+ "add_public_key": description_schema,
"console": {"type": ["string", "null"], "enum": ["novnc", "xvpvnc", "rdp-html5", "spice-html5", None]},
"create-vdu": {
- "type": "list",
+ "type": "array",
"items" :{
"type": "object",
"properties":{
}
},
"delete-vdu": {
- "type": "list",
+ "type": "array",
"items" :{
"type": "object",
"properties":{
__date__ ="$14-aug-2017 23:59:59$"
import logging
+import paramiko
+import socket
+import StringIO
#Error variables
HTTP_Bad_Request = 400
"""
raise vimconnNotImplemented( "SFC support not implemented" )
+ def inject_user_key(self, ip_addr=None, user=None, key=None, ro_key=None, password=None):
+ """
+ Inject a ssh public key in a VM
+ Params:
+ ip_addr: ip address of the VM
+ user: username (default-user) to enter in the VM
+ key: public key to be injected in the VM
+ ro_key: private key of the RO, used to enter in the VM if the password is not provided
+ password: password of the user to enter in the VM
+ The function doesn't return a value:
+ """
+ if not ip_addr or not user:
+ raise vimconnNotSupportedException("All parameters should be different from 'None'")
+ elif not ro_key and not password:
+ raise vimconnNotSupportedException("All parameters should be different from 'None'")
+ else:
+ commands = {'mkdir -p ~/.ssh/', 'echo "%s" >> ~/.ssh/authorized_keys' % key,
+ 'chmod 644 ~/.ssh/authorized_keys', 'chmod 700 ~/.ssh/'}
+ client = paramiko.SSHClient()
+ try:
+ if ro_key:
+ pkey = paramiko.RSAKey.from_private_key(StringIO.StringIO(ro_key))
+ else:
+ pkey = None
+ client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
+ client.connect(ip_addr, username=user, password=password, pkey=pkey, timeout=10)
+ for command in commands:
+ (i, o, e) = client.exec_command(command, timeout=10)
+ returncode = o.channel.recv_exit_status()
+ output = o.read()
+ outerror = e.read()
+ if returncode != 0:
+ text = "run_command='{}' Error='{}'".format(command, outerror)
+ raise vimconnUnexpectedResponse("Cannot inject ssh key in VM: '{}'".format(text))
+ return
+ except (socket.error, paramiko.AuthenticationException, paramiko.SSHException) as message:
+ raise vimconnUnexpectedResponse(
+ "Cannot inject ssh key in VM: '{}' - {}".format(ip_addr, str(message)))
+ return
+
+
#NOT USED METHODS in current version
def host_vim2gui(self, host, server_dict):
"""Adds a VM instance to VIM"""
"""Returns the instance identifier"""
raise vimconnNotImplemented( "Should have implemented this" )
+
"#################################################################\n"\
"##### INSTALL PYTHON PACKAGES #####\n"\
"#################################################################"
- [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-yaml python-bottle python-mysqldb python-jsonschema python-paramiko python-argcomplete python-requests python-logutils libxml2-dev libxslt-dev python-dev python-pip"
- [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "PyYAML MySQL-python python-jsonschema python-paramiko python-argcomplete python-requests python-logutils libxslt-devel libxml2-devel python-devel python-pip"
+ [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-yaml python-bottle python-mysqldb python-jsonschema python-paramiko python-argcomplete python-requests python-logutils libxml2-dev libxslt-dev python-dev python-pip python-crypto"
+ [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "PyYAML MySQL-python python-jsonschema python-paramiko python-argcomplete python-requests python-logutils libxslt-devel libxml2-devel python-devel python-pip python-crypto"
# The only way to install python-bottle on Centos7 is with easy_install or pip
[ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && easy_install -U bottle
"boto",
#"lib_osm_openvim",
#"osm_im",
+ "python-crypto",
]
setup(name=_name,