From e580c7d9d4c0f8a093597d83808f96cdab261b0c Mon Sep 17 00:00:00 2001 From: gcalvino Date: Fri, 22 Sep 2017 14:09:51 +0200 Subject: [PATCH] Feature 1429 - secure key management feature Change-Id: Ic643fcde4b8e1456e8ab1d8c256a971adb7412a2 Signed-off-by: gcalvino --- database_utils/migrate_mano_db.sh | 20 +++- openmano | 2 +- openmanod | 5 +- osm_ro/nfvo.py | 154 +++++++++++++++++++++++------- osm_ro/nfvo_db.py | 21 ++-- osm_ro/openmano_schemas.py | 10 +- osm_ro/vimconn.py | 45 +++++++++ scripts/install-openmano.sh | 4 +- setup.py | 1 + 9 files changed, 210 insertions(+), 52 deletions(-) diff --git a/database_utils/migrate_mano_db.sh b/database_utils/migrate_mano_db.sh index 4a08a423..c5d6ee64 100755 --- a/database_utils/migrate_mano_db.sh +++ b/database_utils/migrate_mano_db.sh @@ -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);" diff --git a/openmano b/openmano index e2221bc4..6af5ea58 100755 --- 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)") diff --git a/openmanod b/openmanod index 39117f7b..841fb7f6 100755 --- 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 diff --git a/osm_ro/nfvo.py b/osm_ro/nfvo.py index d5d2dc66..fcc78994 100644 --- a/osm_ro/nfvo.py +++ b/osm_ro/nfvo.py @@ -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 diff --git a/osm_ro/nfvo_db.py b/osm_ro/nfvo_db.py index cb345e5f..79ba7d7f 100644 --- a/osm_ro/nfvo_db.py +++ b/osm_ro/nfvo_db.py @@ -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) diff --git a/osm_ro/openmano_schemas.py b/osm_ro/openmano_schemas.py index 1e83f4be..9e15ac53 100644 --- a/osm_ro/openmano_schemas.py +++ b/osm_ro/openmano_schemas.py @@ -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":{ diff --git a/osm_ro/vimconn.py b/osm_ro/vimconn.py index 8b6f80ee..ea3117d5 100644 --- a/osm_ro/vimconn.py +++ b/osm_ro/vimconn.py @@ -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" ) + diff --git a/scripts/install-openmano.sh b/scripts/install-openmano.sh index 34eb6ee1..bab698f7 100755 --- a/scripts/install-openmano.sh +++ b/scripts/install-openmano.sh @@ -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 diff --git a/setup.py b/setup.py index 70cefd46..a34c7088 100755 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ _requirements = [ "boto", #"lib_osm_openvim", #"osm_im", + "python-crypto", ] setup(name=_name, -- 2.17.1