Feature 1429 - secure key management feature 83/2283/17
authorgcalvino <guillermo.calvinosanchez@altran.com>
Fri, 22 Sep 2017 12:09:51 +0000 (14:09 +0200)
committergcalvino <guillermo.calvinosanchez@altran.com>
Mon, 2 Oct 2017 08:20:12 +0000 (10:20 +0200)
Change-Id: Ic643fcde4b8e1456e8ab1d8c256a971adb7412a2
Signed-off-by: gcalvino <guillermo.calvinosanchez@altran.com>
database_utils/migrate_mano_db.sh
openmano
openmanod
osm_ro/nfvo.py
osm_ro/nfvo_db.py
osm_ro/openmano_schemas.py
osm_ro/vimconn.py
scripts/install-openmano.sh
setup.py

index 4a08a42..c5d6ee6 100755 (executable)
@@ -33,7 +33,7 @@ DBPORT="3306"
 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)
@@ -194,6 +194,7 @@ fi
 #[ $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(){
@@ -807,6 +808,7 @@ function downgrade_from_23(){
 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');"
@@ -1003,6 +1005,22 @@ function downgrade_from_26(){
     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);"
index e2221bc..6af5ea5 100755 (executable)
--- a/openmano
+++ b/openmano
@@ -1767,7 +1767,7 @@ if __name__=="__main__":
     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)")
index 39117f7..841fb7f 100755 (executable)
--- a/openmanod
+++ b/openmanod
@@ -48,9 +48,10 @@ import osm_ro
 
 __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
index d5d2dc6..fcc7899 100644 (file)
@@ -46,10 +46,10 @@ from threading import Lock
 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
 
@@ -958,7 +958,7 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                 # 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"]]
@@ -1077,11 +1077,19 @@ def new_vnfd_v3(mydb, tenant_id, vnf_descriptor):
                 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},
@@ -2386,7 +2394,6 @@ def start_scenario(mydb, tenant_id, scenario_id, instance_scenario_name, instanc
         #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
@@ -2536,7 +2543,6 @@ def update(d, u):
             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...")
@@ -2549,7 +2555,9 @@ def create_instance(mydb, tenant_id, instance_dict):
     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"
@@ -2644,6 +2652,10 @@ def create_instance(mydb, tenant_id, instance_dict):
 
         # 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)
@@ -3442,7 +3454,6 @@ def refresh_instance(mydb, nfvo_tenant, instanceDict, datacenter=None, vim_tenan
 
     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)
@@ -3475,44 +3486,69 @@ def instance_action(mydb,nfvo_tenant,instance_id, action_dict):
                                 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
@@ -3568,11 +3604,18 @@ def check_tenant(mydb, tenant_id):
         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
@@ -4331,3 +4374,42 @@ def datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id):
 
 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
index cb345e5..79ba7d7 100644 (file)
@@ -213,7 +213,7 @@ class nfvo_db(db_base.db_base):
                     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.
@@ -586,7 +586,16 @@ class nfvo_db(db_base.db_base):
                     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"\
@@ -721,7 +730,7 @@ class nfvo_db(db_base.db_base):
                     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")
@@ -923,9 +932,9 @@ class nfvo_db(db_base.db_base):
                         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)
@@ -934,7 +943,7 @@ class nfvo_db(db_base.db_base):
                     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)
index 1e83f4b..9e15ac5 100644 (file)
@@ -546,7 +546,7 @@ vnfc_schema = {
         "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",
@@ -598,6 +598,7 @@ vnfd_schema_v01 = {
                 "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},
@@ -611,7 +612,7 @@ vnfd_schema_v01 = {
     "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#",
@@ -1097,9 +1098,10 @@ instance_scenario_action_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":{
@@ -1111,7 +1113,7 @@ instance_scenario_action_schema = {
             }
         },
         "delete-vdu": {
-            "type": "list",
+            "type": "array",
             "items" :{
                 "type": "object",
                 "properties":{
index 8b6f80e..ea3117d 100644 (file)
@@ -29,6 +29,9 @@ __author__="Alfonso Tierno, Igor D.C."
 __date__ ="$14-aug-2017 23:59:59$"
 
 import logging
+import paramiko
+import socket
+import StringIO
 
 #Error variables 
 HTTP_Bad_Request = 400
@@ -662,6 +665,47 @@ class vimconnector():
         """
         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):
@@ -708,3 +752,4 @@ class vimconnector():
         """Adds a VM instance to VIM"""
         """Returns the instance identifier"""
         raise vimconnNotImplemented( "Should have implemented this" )
+
index 34eb6ee..bab698f 100755 (executable)
@@ -233,8 +233,8 @@ then
         "#################################################################\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
 
index 70cefd4..a34c708 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -37,6 +37,7 @@ _requirements = [
     "boto",
     #"lib_osm_openvim",
     #"osm_im",
+    "python-crypto",
 ]
 
 setup(name=_name,