Merge: openstack client for keystone v3. Maintain sessions upon class reload with... 51/1951/1
authortierno <alfonso.tiernosepulveda@telefonica.com>
Tue, 20 Jun 2017 10:21:48 +0000 (12:21 +0200)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Tue, 20 Jun 2017 10:21:48 +0000 (12:21 +0200)
Signed-off-by: tierno <alfonso.tiernosepulveda@telefonica.com>
19 files changed:
Dockerfile
database_utils/migrate_mano_db.sh
openmano
openmanod
osm_ro/httpserver.py
osm_ro/nfvo.py
osm_ro/nfvo_db.py
osm_ro/openmano_schemas.py
osm_ro/vim_thread.py
osm_ro/vimconn_aws.py
osm_ro/vimconn_openstack.py
osm_ro/vimconn_openvim.py
osm_ro/vimconn_vmware.py
requirements.txt
scripts/install-openmano.sh
setup.py
stdeb.cfg
test/basictest.sh
test/test_RO.py

index 84144a8..82c3ca9 100644 (file)
@@ -6,5 +6,5 @@ RUN  apt-get update && \
   DEBIAN_FRONTEND=noninteractive apt-get -y install python-pip libmysqlclient-dev libssl-dev libffi-dev && \
   DEBIAN_FRONTEND=noninteractive pip install --upgrade pip && \
   DEBIAN_FRONTEND=noninteractive pip install --upgrade setuptools && \
-  DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-boto python-bottle python-jsonschema python-logutils python-cinderclient python-glanceclient python-keystoneclient python-neutronclient python-novaclient python-mysqldb
+  DEBIAN_FRONTEND=noninteractive apt-get -y install python-argcomplete python-boto python-bottle python-jsonschema python-logutils python-cinderclient python-glanceclient python-keystoneclient python-neutronclient python-novaclient python-openstackclient python-mysqldb
 
index 27e7a89..d57194e 100755 (executable)
@@ -33,7 +33,7 @@ DBPORT="3306"
 DBNAME="mano_db"
 QUIET_MODE=""
 #TODO update it with the last database version
-LAST_DB_VERSION=20
+LAST_DB_VERSION=21
  
 # Detect paths
 MYSQL=$(which mysql)
@@ -188,6 +188,7 @@ fi
 #[ $OPENMANO_VER_NUM -ge 5004 ] && DB_VERSION=18  #0.5.4 =>  18
 #[ $OPENMANO_VER_NUM -ge 5005 ] && DB_VERSION=19  #0.5.5 =>  19
 #[ $OPENMANO_VER_NUM -ge 5009 ] && DB_VERSION=20  #0.5.9 =>  20
+#[ $OPENMANO_VER_NUM -ge 5015 ] && DB_VERSION=21  #0.5.15 =>  21
 #TODO ... put next versions here
 
 function upgrade_to_1(){
@@ -751,6 +752,26 @@ function downgrade_from_20(){
     echo "DELETE FROM schema_version WHERE version_int='20';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
 }
 
+function upgrade_to_21(){
+    # echo "    upgrade database from version 0.20 to version 0.21"
+    echo "      edit 'instance_nets' to allow instance_scenario_id=None"
+    echo "ALTER TABLE instance_nets MODIFY COLUMN instance_scenario_id varchar(36) NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "      enlarge column 'dns_address' at table 'ip_profiles'"
+    echo "ALTER TABLE ip_profiles MODIFY dns_address varchar(255) DEFAULT NULL NULL "\
+         "comment 'dns ip list separated by semicolon';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "INSERT INTO schema_version (version_int, version, openmano_ver, comments, date) VALUES (21, '0.21', '0.5.15', 'Edit instance_nets to allow instance_scenario_id=None and enlarge column dns_address at table ip_profiles', '2017-06-02');" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
+function downgrade_from_21(){
+    # echo "    downgrade database from version 0.21 to version 0.20"
+    echo "      edit 'instance_nets' to disallow instance_scenario_id=None"
+    #Delete all lines with a instance_scenario_id=NULL in order to disable this option
+    echo "DELETE FROM instance_nets WHERE instance_scenario_id IS NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "ALTER TABLE instance_nets MODIFY COLUMN instance_scenario_id varchar(36) NOT NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "      shorten column 'dns_address' at table 'ip_profiles'"
+    echo "ALTER TABLE ip_profiles MODIFY dns_address varchar(64) DEFAULT NULL NULL;" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+    echo "DELETE FROM schema_version WHERE version_int='21';" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
+}
+
 function upgrade_to_X(){
     echo "      change 'datacenter_nets'"
     echo "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);" | $DBCMD || ! echo "ERROR. Aborted!" || exit -1
index 7b33cb4..6c0d15b 100755 (executable)
--- a/openmano
+++ b/openmano
 '''
 openmano client used to interact with openmano-server (openmanod) 
 '''
-__author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
-__date__ ="$09-oct-2014 09:09:48$"
-__version__="0.4.13-r519"
-version_date="Mar 2017"
+__author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes"
+__date__ = "$09-oct-2014 09:09:48$"
+__version__ = "0.4.14-r521"
+version_date = "May 2017"
 
 from argcomplete.completers import FilesCompleter
 import os
@@ -141,6 +141,9 @@ def _print_verbose(mano_response, verbose_level=0):
             if verbose_level >=1:
                 if content.get('created_at'):
                     myoutput += " " + content['created_at'].ljust(20)
+                if content.get('sdn_attached_ports'):
+                    #myoutput += " " + str(content['sdn_attached_ports']).ljust(20)
+                    myoutput += "\nsdn_attached_ports:\n" + yaml.safe_dump(content['sdn_attached_ports'], indent=4, default_flow_style=False)
                 if verbose_level >=2:
                     new_line='\n'
                     if content.get('type'):
@@ -556,6 +559,7 @@ def scenario_deploy(args):
 
 def scenario_verify(args):
     #print "scenario-verify",args
+    tenant = _get_tenant()
     headers_req = {'content-type': 'application/json'}
     action = {}
     action["verify"] = {}
@@ -563,7 +567,7 @@ def scenario_verify(args):
     payload_req = json.dumps(action, indent=4)
     #print payload_req
 
-    URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, mano_tenant, args.scenario)
+    URLrequest = "http://%s:%s/openmano/%s/scenarios/%s/action" %(mano_host, mano_port, tenant, args.scenario)
     logger.debug("openmano request: %s", payload_req)
     mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
     logger.debug("openmano response: %s", mano_response.text )
@@ -1056,6 +1060,8 @@ def datacenter_sdn_port_mapping_set(args):
             "No yaml/json has been provided specifying the SDN port mapping")
 
     port_mapping = yaml.load(datacenter_sdn_port_mapping_list(args))
+    if 'error' in port_mapping:
+        raise OpenmanoCLIError("openmano client error: {}".format(port_mapping['error']['description']))
     if len(port_mapping["sdn_port_mapping"]["ports_mapping"]) > 0:
         if not args.force:
             r = raw_input("Datacenter %s already contains a port mapping. Overwrite? (y/N)? " % (datacenter))
@@ -1271,13 +1277,89 @@ def vim_action(args):
             return
         payload_req = yaml.safe_dump(create_dict, explicit_start=True, indent=4, default_flow_style=False, tags=False, encoding='utf-8', allow_unicode=True)
         logger.debug("openmano request: %s", payload_req)
-        URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, mano_tenant, datacenter, args.item)
+        URLrequest = "http://%s:%s/openmano/%s/vim/%s/%ss" %(mano_host, mano_port, tenant, datacenter, args.item)
         mano_response = requests.post(URLrequest, headers = headers_req, data=payload_req)
         logger.debug("openmano response: %s", mano_response.text )
         if args.verbose==None:
             args.verbose=0
         return _print_verbose(mano_response, args.verbose)
 
+def _get_items(item, item_name_id=None, datacenter=None, tenant=None):
+    URLrequest = "http://%s:%s/openmano" %(mano_host, mano_port)
+    if tenant:
+        URLrequest += "/" + tenant
+    if datacenter:
+        URLrequest += "/vim/" + datacenter
+    if item:
+        URLrequest += "/" + item +"s"
+    if item_name_id:
+        URLrequest += "/" + item_name_id
+    mano_response = requests.get(URLrequest)
+    logger.debug("openmano response: %s", mano_response.text )
+
+    return mano_response
+
+def vim_net_sdn_attach(args):
+    #Verify the network exists in the vim
+    tenant = _get_tenant()
+    datacenter = _get_datacenter(args.datacenter, tenant)
+    result = _get_items('network', item_name_id=args.vim_net, datacenter=datacenter, tenant=tenant)
+    content = yaml.load(result.content)
+    if 'networks' in content:
+        raise OpenmanoCLIError('More than one network in the vim named ' + args.vim_net + '. Use uuid instead')
+    if 'error' in content:
+        raise OpenmanoCLIError(yaml.safe_dump(content))
+    network_uuid = content['network']['id']
+
+    #Make call to attach the dataplane port to the SND network associated to the vim network
+    headers_req = {'content-type': 'application/yaml'}
+    payload_req = {'port': args.port}
+    if args.vlan:
+        payload_req['vlan'] = int(args.vlan)
+    if args.mac:
+        payload_req['mac'] = args.mac
+
+    URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/attach" % (mano_host, mano_port, tenant, datacenter, network_uuid)
+    logger.debug("openmano request: %s", payload_req)
+    mano_response = requests.post(URLrequest, headers=headers_req, data=json.dumps(payload_req))
+    logger.debug("openmano response: %s", mano_response.text)
+    result = _print_verbose(mano_response, args.verbose)
+
+    return
+
+
+def vim_net_sdn_detach(args):
+    if not args.all and not args.id:
+        print "--all or --id must be used"
+        return 1
+
+    # Verify the network exists in the vim
+    tenant = _get_tenant()
+    datacenter = _get_datacenter(args.datacenter, tenant)
+    result = _get_items('network', item_name_id=args.vim_net, datacenter=datacenter, tenant=tenant)
+    content = yaml.load(result.content)
+    if 'networks' in content:
+        raise OpenmanoCLIError('More than one network in the vim named ' + args.vim_net + '. Use uuid instead')
+    if 'error' in content:
+        raise OpenmanoCLIError(yaml.safe_dump(content))
+    network_uuid = content['network']['id']
+
+    if not args.force:
+        r = raw_input("Confirm action' (y/N)? ")
+        if len(r) == 0 or r[0].lower() != "y":
+            return 0
+
+    if args.id:
+        URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/detach/%s" % (
+            mano_host, mano_port, tenant, datacenter, network_uuid, args.id)
+    else:
+        URLrequest = "http://%s:%s/openmano/%s/vim/%s/network/%s/detach" % (
+            mano_host, mano_port, tenant, datacenter, network_uuid)
+    mano_response = requests.delete(URLrequest)
+    logger.debug("openmano response: %s", mano_response.text)
+    result = _print_verbose(mano_response, args.verbose)
+
+    return
 
 def datacenter_net_action(args):
     if args.action == "net-update":
@@ -1797,24 +1879,50 @@ if __name__=="__main__":
         if item=='netmap-import':
             datacenter_action_parser.add_argument("-f","--force", action="store_true", help="do not prompt for confirmation")
         datacenter_action_parser.set_defaults(func=datacenter_netmap_action, action=item)
-    
+
+    # =======================vim_net_sdn_xxx section=======================
+    # vim_net_sdn_attach
+    vim_net_sdn_attach_parser = subparsers.add_parser('vim-net-sdn-attach',
+                                                      parents=[parent_parser],
+                                                      help="Specify the port to access to an external network using SDN")
+    vim_net_sdn_attach_parser.add_argument("vim_net", action="store",
+                                                help="Name/id of the network in the vim that will be used to connect to the external network")
+    vim_net_sdn_attach_parser.add_argument("port", action="store", help="Specifies the port in the dataplane switch to access to the external network")
+    vim_net_sdn_attach_parser.add_argument("--vlan", action="store", help="Specifies the vlan (if any) to use in the defined port")
+    vim_net_sdn_attach_parser.add_argument("--mac", action="store", help="Specifies the MAC (if known) of the physical device that will be reachable by this external port")
+    vim_net_sdn_attach_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
+    vim_net_sdn_attach_parser.set_defaults(func=vim_net_sdn_attach)
+
+    # vim_net_sdn_detach
+    vim_net_sdn_detach_parser = subparsers.add_parser('vim-net-sdn-detach',
+                                                           parents=[parent_parser],
+                                                           help="Remove the port information to access to an external network using SDN")
+
+    vim_net_sdn_detach_parser.add_argument("vim_net", action="store", help="Name/id of the vim network")
+    vim_net_sdn_detach_parser.add_argument("--id", action="store",help="Specify the uuid of the external ports from this network to be detached")
+    vim_net_sdn_detach_parser.add_argument("--all", action="store_true", help="Detach all external ports from this network")
+    vim_net_sdn_detach_parser.add_argument("-f", "--force", action="store_true", help="forces clearing without asking")
+    vim_net_sdn_detach_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
+    vim_net_sdn_detach_parser.set_defaults(func=vim_net_sdn_detach)
+    # =======================
+
     for item in ("network", "tenant", "image"):
         if item=="network":
-            commnad_name = 'vim-net'
+            command_name = 'vim-net'
         else:
-            commnad_name = 'vim-'+item
-        vim_item_list_parser = subparsers.add_parser(commnad_name + '-list', parents=[parent_parser], help="list the vim " + item + "s")
+            command_name = 'vim-'+item
+        vim_item_list_parser = subparsers.add_parser(command_name + '-list', parents=[parent_parser], help="list the vim " + item + "s")
         vim_item_list_parser.add_argument("name", nargs='?', help="name or uuid of the " + item + "s")
         vim_item_list_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
         vim_item_list_parser.set_defaults(func=vim_action, item=item, action="list")
 
-        vim_item_del_parser = subparsers.add_parser(commnad_name + '-delete', parents=[parent_parser], help="list the vim " + item + "s")
+        vim_item_del_parser = subparsers.add_parser(command_name + '-delete', parents=[parent_parser], help="list the vim " + item + "s")
         vim_item_del_parser.add_argument("name", help="name or uuid of the " + item + "s")
         vim_item_del_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
         vim_item_del_parser.set_defaults(func=vim_action, item=item, action="delete")
 
         if item == "network" or item == "tenant":
-            vim_item_create_parser = subparsers.add_parser(commnad_name + '-create', parents=[parent_parser], help="create a "+item+" at vim")
+            vim_item_create_parser = subparsers.add_parser(command_name + '-create', parents=[parent_parser], help="create a "+item+" at vim")
             vim_item_create_parser.add_argument("file", nargs='?', help="descriptor of the %s. Must be a file or yaml/json text" % item).completer = FilesCompleter
             vim_item_create_parser.add_argument("--name", action="store", help="name of the %s" % item  )
             vim_item_create_parser.add_argument("--datacenter", action="store", help="specifies the datacenter")
index 0ad7a9e..8f8c3f1 100755 (executable)
--- a/openmanod
+++ b/openmanod
@@ -48,9 +48,9 @@ import osm_ro
 
 __author__ = "Alfonso Tierno, Gerardo Garcia, Pablo Montes"
 __date__ = "$26-aug-2014 11:09:29$"
-__version__ = "0.5.12-r522"
-version_date = "May 2017"
-database_version = 20      #expected database schema version
+__version__ = "0.5.15-r524"
+version_date = "Jun 2017"
+database_version = 21      #expected database schema version
 
 global global_config
 global logger
@@ -77,27 +77,30 @@ def load_configuration(configuration_file):
         config = yaml.load(config_str)
         #Validate configuration file with the config_schema
         js_v(config, config_schema)
-        
+
         #Add default values tokens
         for k,v in default_tokens.items():
             if k not in config:
                 config[k]=v
         return config
-    
+
     except yaml.YAMLError as e:
         error_pos = ""
         if hasattr(e, 'problem_mark'):
             mark = e.problem_mark
             error_pos = " at line:{} column:{}".format(mark.line+1, mark.column+1)
-        raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}".format(file=configuration_file, pos=error_pos) )
+        raise LoadConfigurationException("Bad YAML format at configuration file '{file}'{pos}: {message}".format(
+            file=configuration_file, pos=error_pos, message=e))
     except js_e.ValidationError as e:
         error_pos = ""
         if e.path:
             error_pos=" at '" + ":".join(map(str, e.path))+"'"
-        raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(file=configuration_file, pos=error_pos, message=str(e)) ) 
+        raise LoadConfigurationException("Invalid field at configuration file '{file}'{pos} {message}".format(
+            file=configuration_file, pos=error_pos, message=e))
     except Exception as e:
-        raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(file=configuration_file, message=str(e) ) )
-                
+        raise LoadConfigurationException("Cannot load configuration file '{file}' {message}".format(
+            file=configuration_file, message=e))
+
 
 def console_port_iterator():
     '''this iterator deals with the http_console_ports 
@@ -117,8 +120,8 @@ def console_port_iterator():
                 yield port2
                 port2 += 1
         index += 1
-    
-    
+
+
 def usage():
     print("Usage: ", sys.argv[0], "[options]")
     print( "      -v|--version: prints current version")
@@ -131,23 +134,37 @@ def usage():
     print( "      --log-socket-port PORT: send logs using this port (default: 9022)")
     print( "      --log-file FILE: send logs to this file")
     return
-    
+
+
+def set_logging_file(log_file):
+    try:
+        file_handler = logging.handlers.RotatingFileHandler(log_file, maxBytes=100e6, backupCount=9, delay=0)
+        file_handler.setFormatter(log_formatter_simple)
+        logger.addHandler(file_handler)
+        # logger.debug("moving logs to '%s'", global_config["log_file"])
+        # remove initial stream handler
+        logging.root.removeHandler(logging.root.handlers[0])
+        print ("logging on '{}'".format(log_file))
+    except IOError as e:
+        raise LoadConfigurationException(
+            "Cannot open logging file '{}': {}. Check folder exist and permissions".format(log_file, e))
+
+
 if __name__=="__main__":
-    #Configure logging step 1
+    # Configure logging step 1
     hostname = socket.gethostname()
-    #streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
+    # streamformat = "%(levelname)s (%(module)s:%(lineno)d) %(message)s"
     # "%(asctime)s %(name)s %(levelname)s %(filename)s:%(lineno)d %(funcName)s %(process)d: %(message)s"
-    log_formatter_complete = logging.Formatter(
-        '%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s severity:%(levelname)s logger:%(name)s log:%(message)s'.format(host=hostname),
-        datefmt='%Y-%m-%dT%H:%M:%S',
-    )
+    log_formatter_complete = logging.Formatter('%(asctime)s.%(msecs)03d00Z[{host}@openmanod] %(filename)s:%(lineno)s '
+                                               'severity:%(levelname)s logger:%(name)s log:%(message)s'.format(
+                                                    host=hostname),
+                                               datefmt='%Y-%m-%dT%H:%M:%S')
     log_format_simple =  "%(asctime)s %(levelname)s  %(name)s %(filename)s:%(lineno)s %(message)s"
     log_formatter_simple = logging.Formatter(log_format_simple, datefmt='%Y-%m-%dT%H:%M:%S')
     logging.basicConfig(format=log_format_simple, level= logging.DEBUG)
     logger = logging.getLogger('openmano')
     logger.setLevel(logging.DEBUG)
     socket_handler = None
-    file_handler = None
     # Read parameters and configuration file
     httpthread = None
     try:
@@ -160,7 +177,7 @@ if __name__=="__main__":
         log_file = None
         log_socket_host = None
         log_socket_port = None
-        
+
         for o, a in opts:
             if o in ("-v", "--version"):
                 print ("openmanod version " + __version__ + ' ' + version_date)
@@ -185,6 +202,8 @@ if __name__=="__main__":
                 log_file = a
             else:
                 assert False, "Unhandled option"
+        if log_file:
+            set_logging_file(log_file)
         global_config = load_configuration(config_file)
         global_config["version"] = __version__
         global_config["version_date"] = version_date
@@ -198,12 +217,10 @@ if __name__=="__main__":
             global_config['log_socket_host'] = log_socket_host
         if log_socket_port:
             global_config['log_socket_port'] = log_socket_port
-        if log_file:
-            global_config['log_file'] = log_file
 #         if vnf_repository is not None:
 #             global_config['vnf_repository'] = vnf_repository
 #         else:
-#             if not 'vnf_repository' in global_config:  
+#             if not 'vnf_repository' in global_config:
 #                 logger.error( os.getcwd() )
 #                 global_config['vnf_repository'] = os.getcwd()+'/vnfrepo'
 #         #print global_config
@@ -214,7 +231,7 @@ if __name__=="__main__":
 #             except Exception as e:
 #                 logger.error( "Error '%s'. Ensure the path 'vnf_repository' is properly set at %s",e.args[1], config_file)
 #                 exit(-1)
-        
+
         global_config["console_port_iterator"] = console_port_iterator
         global_config["console_thread"]={}
         global_config["console_ports"]={}
@@ -222,31 +239,26 @@ if __name__=="__main__":
             global_config["http_console_host"] = global_config["http_host"]
             if global_config["http_host"]=="0.0.0.0":
                 global_config["http_console_host"] = socket.gethostname()
-        
-        #Configure logging STEP 2
+
+        # Configure logging STEP 2
         if "log_host" in global_config:
             socket_handler= log_handlers.SocketHandler(global_config["log_socket_host"], global_config["log_socket_port"])
             socket_handler.setFormatter(log_formatter_complete)
-            if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]: 
+            if global_config.get("log_socket_level") and global_config["log_socket_level"] != global_config["log_level"]:
                 socket_handler.setLevel(global_config["log_socket_level"])
             logger.addHandler(socket_handler)
-        #logger.addHandler(log_handlers.SysLogHandler())
-        if "log_file" in global_config:
-            try:
-                file_handler= logging.handlers.RotatingFileHandler(global_config["log_file"], maxBytes=100e6, backupCount=9, delay=0)
-                file_handler.setFormatter(log_formatter_simple)
-                logger.addHandler(file_handler)
-                #logger.debug("moving logs to '%s'", global_config["log_file"])
-                #remove initial stream handler
-                logging.root.removeHandler(logging.root.handlers[0])
-                print ("logging on '{}'".format(global_config["log_file"]))
-            except IOError as e:
-                raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config["log_file"], str(e)) ) 
-        #logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
+
+        # logger.addHandler(log_handlers.SysLogHandler())
+        if log_file:
+            global_config['log_file'] = log_file
+        elif global_config.get('log_file'):
+            set_logging_file(global_config['log_file'])
+
+        # logging.basicConfig(level = getattr(logging, global_config.get('log_level',"debug")))
         logger.setLevel(getattr(logging, global_config['log_level']))
-        logger.critical("Starting openmano server version: '%s %s' command: '%s'",  
+        logger.critical("Starting openmano server version: '%s %s' command: '%s'",
                          __version__, version_date, " ".join(sys.argv))
-        
+
         for log_module in ("nfvo", "http", "vim", "db", "console", "ovim"):
             log_level_module = "log_level_" + log_module
             log_file_module = "log_file_" + log_module
@@ -255,15 +267,18 @@ if __name__=="__main__":
                 logger_module.setLevel(global_config[log_level_module])
             if log_file_module in global_config:
                 try:
-                    file_handler= logging.handlers.RotatingFileHandler(global_config[log_file_module], maxBytes=100e6, backupCount=9, delay=0)
+                    file_handler = logging.handlers.RotatingFileHandler(global_config[log_file_module],
+                                                                        maxBytes=100e6, backupCount=9, delay=0)
                     file_handler.setFormatter(log_formatter_simple)
                     logger_module.addHandler(file_handler)
                 except IOError as e:
-                    raise LoadConfigurationException("Cannot open logging file '{}': {}. Check folder exist and permissions".format(global_config[log_file_module], str(e)) ) 
+                    raise LoadConfigurationException(
+                        "Cannot open logging file '{}': {}. Check folder exist and permissions".format(
+                            global_config[log_file_module], str(e)) )
             global_config["logger_"+log_module] = logger_module
         #httpserver.logger = global_config["logger_http"]
         #nfvo.logger = global_config["logger_nfvo"]
-        
+
         # Initialize DB connection
         mydb = nfvo_db.nfvo_db();
         mydb.connect(global_config['db_host'], global_config['db_user'], global_config['db_passwd'], global_config['db_name'])
@@ -284,14 +299,14 @@ if __name__=="__main__":
 
         nfvo.global_config=global_config
         nfvo.start_service(mydb)
-        
+
         httpthread = httpserver.httpserver(mydb, False, global_config['http_host'], global_config['http_port'])
-        
+
         httpthread.start()
-        if 'http_admin_port' in global_config: 
+        if 'http_admin_port' in global_config:
             httpthreadadmin = httpserver.httpserver(mydb, True, global_config['http_host'], global_config['http_admin_port'])
             httpthreadadmin.start()
-        time.sleep(1)      
+        time.sleep(1)
         logger.info('Waiting for http clients')
         print('Waiting for http clients')
         print('openmanod ready')
@@ -321,6 +336,9 @@ if __name__=="__main__":
     except db_base_Exception as e:
         logger.critical(str(e))
         exit(-1)
+    except nfvo.NfvoException as e:
+        logger.critical(str(e), exc_info=True)
+        exit(-1)
     nfvo.stop_service()
     if httpthread:
         httpthread.join(1)
index d6dc0c7..94544f6 100644 (file)
@@ -43,7 +43,7 @@ from openmano_schemas import vnfd_schema_v01, vnfd_schema_v02, \
                             tenant_schema, tenant_edit_schema,\
                             datacenter_schema, datacenter_edit_schema, datacenter_action_schema, datacenter_associate_schema,\
                             object_schema, netmap_new_schema, netmap_edit_schema, sdn_controller_schema, sdn_controller_edit_schema, \
-                            sdn_port_mapping_schema
+                            sdn_port_mapping_schema, sdn_external_port_schema
 
 import nfvo
 import utils
@@ -915,6 +915,33 @@ def http_deassociate_datacenters(tenant_id, datacenter_id):
         logger.error("Unexpected exception: ", exc_info=True)
         bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
 
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/attach', method='POST')
+def http_post_vim_net_sdn_attach(tenant_id, datacenter_id, network_id):
+    logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+    http_content, _ = format_in(sdn_external_port_schema)
+    try:
+        data = nfvo.vim_net_sdn_attach(mydb, tenant_id, datacenter_id, network_id, http_content)
+        return format_out(data)
+    except (nfvo.NfvoException, db_base_Exception) as e:
+        logger.error("http_post_vim_net_sdn_attach error {}: {}".format(e.http_code, str(e)))
+        bottle.abort(e.http_code, str(e))
+    except Exception as e:
+        logger.error("Unexpected exception: ", exc_info=True)
+        bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
+
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach', method='DELETE')
+@bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/network/<network_id>/detach/<port_id>', method='DELETE')
+def http_delete_vim_net_sdn_detach(tenant_id, datacenter_id, network_id, port_id=None):
+    logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+    try:
+        data = nfvo.vim_net_sdn_detach(mydb, tenant_id, datacenter_id, network_id, port_id)
+        return format_out(data)
+    except (nfvo.NfvoException, db_base_Exception) as e:
+        logger.error("http_delete_vim_net_sdn_detach error {}: {}".format(e.http_code, str(e)))
+        bottle.abort(e.http_code, str(e))
+    except Exception as e:
+        logger.error("Unexpected exception: ", exc_info=True)
+        bottle.abort(HTTP_Internal_Server_Error, type(e).__name__ + ": " + str(e))
        
 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>', method='GET')
 @bottle.route(url_base + '/<tenant_id>/vim/<datacenter_id>/<item>/<name>', method='GET')
index 5a29eed..d45a3c7 100644 (file)
@@ -44,6 +44,7 @@ import nfvo_db
 from threading import Lock
 from time import time
 from lib_osm_openvim import ovim as ovim_module
+from lib_osm_openvim.ovim import ovimException
 
 global global_config
 global vimconn_imported
@@ -128,14 +129,16 @@ def start_service(mydb):
         #TODO: log_level_of should not be needed. To be modified in ovim
         'log_level_of': 'DEBUG'
     }
-    ovim = ovim_module.ovim(ovim_configuration)
-    ovim.start_service()
-
-    from_= 'tenants_datacenters as td join datacenters as d on td.datacenter_id=d.uuid join datacenter_tenants as dt on td.datacenter_tenant_id=dt.uuid'
-    select_ = ('type','d.config as config','d.uuid as datacenter_id', 'vim_url', 'vim_url_admin', 'd.name as datacenter_name',
-                   'dt.uuid as datacenter_tenant_id','dt.vim_tenant_name as vim_tenant_name','dt.vim_tenant_id as vim_tenant_id',
-                   'user','passwd', 'dt.config as dt_config', 'nfvo_tenant_id')
     try:
+        ovim = ovim_module.ovim(ovim_configuration)
+        ovim.start_service()
+
+        from_= 'tenants_datacenters as td join datacenters as d on td.datacenter_id=d.uuid join '\
+                'datacenter_tenants as dt on td.datacenter_tenant_id=dt.uuid'
+        select_ = ('type', 'd.config as config', 'd.uuid as datacenter_id', 'vim_url', 'vim_url_admin',
+                   'd.name as datacenter_name', 'dt.uuid as datacenter_tenant_id',
+                   'dt.vim_tenant_name as vim_tenant_name', 'dt.vim_tenant_id as vim_tenant_id',
+                   'user', 'passwd', 'dt.config as dt_config', 'nfvo_tenant_id')
         vims = mydb.get_rows(FROM=from_, SELECT=select_)
         for vim in vims:
             extra={'datacenter_tenant_id': vim.get('datacenter_tenant_id'),
@@ -172,14 +175,25 @@ def start_service(mydb):
                     config=extra, persistent_info=vim_persistent_info[thread_id]
                 )
             except Exception as e:
-                raise NfvoException("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, str(e)), HTTP_Internal_Server_Error)
-            thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['vim_tenant_id'], vim['vim_tenant_name'], vim['vim_tenant_id'])
+                raise NfvoException("Error at VIM  {}; {}: {}".format(vim["type"], type(e).__name__, e),
+                                    HTTP_Internal_Server_Error)
+            thread_name = get_non_used_vim_name(vim['datacenter_name'], vim['vim_tenant_id'], vim['vim_tenant_name'],
+                                                vim['vim_tenant_id'])
             new_thread = vim_thread.vim_thread(myvim, task_lock, thread_name, vim['datacenter_name'],
                                                vim['datacenter_tenant_id'], db=db, db_lock=db_lock, ovim=ovim)
             new_thread.start()
             vim_threads["running"][thread_id] = new_thread
     except db_base_Exception as e:
         raise NfvoException(str(e) + " at nfvo.get_vim", e.http_code)
+    except ovim_module.ovimException as e:
+        message = str(e)
+        if message[:22] == "DATABASE wrong version":
+            message = "DATABASE wrong version of lib_osm_openvim {msg} -d{dbname} -u{dbuser} -p{dbpass} {ver}' "\
+                      "at host {dbhost}".format(
+                            msg=message[22:-3], dbname=global_config["db_ovim_name"],
+                            dbuser=global_config["db_ovim_user"], dbpass=global_config["db_ovim_passwd"],
+                            ver=message[-3:-1], dbhost=global_config["db_ovim_host"])
+        raise NfvoException(message, HTTP_Bad_Request)
 
 
 def stop_service():
@@ -2027,20 +2041,23 @@ def create_instance(mydb, tenant_id, instance_dict):
             for scenario_net in scenarioDict['nets']:
                 if net_name == scenario_net["name"]:
                     if 'ip-profile' in net_instance_desc:
-                        ipprofile = net_instance_desc['ip-profile']
-                        ipprofile['subnet_address'] = ipprofile.pop('subnet-address',None)
-                        ipprofile['ip_version'] = ipprofile.pop('ip-version','IPv4')
-                        ipprofile['gateway_address'] = ipprofile.pop('gateway-address',None)
-                        ipprofile['dns_address'] = ipprofile.pop('dns-address',None)
-                        if 'dhcp' in ipprofile:
-                            ipprofile['dhcp_start_address'] = ipprofile['dhcp'].get('start-address',None)
-                            ipprofile['dhcp_enabled'] = ipprofile['dhcp'].get('enabled',True)
-                            ipprofile['dhcp_count'] = ipprofile['dhcp'].get('count',None)
-                            del ipprofile['dhcp']
+                        # translate from input format to database format
+                        ipprofile_in = net_instance_desc['ip-profile']
+                        ipprofile_db = {}
+                        ipprofile_db['subnet_address'] = ipprofile_in.get('subnet-address')
+                        ipprofile_db['ip_version'] = ipprofile_in.get('ip-version', 'IPv4')
+                        ipprofile_db['gateway_address'] = ipprofile_in.get('gateway-address')
+                        ipprofile_db['dns_address'] = ipprofile_in.get('dns-address')
+                        if isinstance(ipprofile_db['dns_address'], (list, tuple)):
+                            ipprofile_db['dns_address'] = ";".join(ipprofile_db['dns_address'])
+                        if 'dhcp' in ipprofile_in:
+                            ipprofile_db['dhcp_start_address'] = ipprofile_in['dhcp'].get('start-address')
+                            ipprofile_db['dhcp_enabled'] = ipprofile_in['dhcp'].get('enabled', True)
+                            ipprofile_db['dhcp_count'] = ipprofile_in['dhcp'].get('count' )
                         if 'ip_profile' not in scenario_net:
-                            scenario_net['ip_profile'] = ipprofile
+                            scenario_net['ip_profile'] = ipprofile_db
                         else:
-                            update(scenario_net['ip_profile'],ipprofile)
+                            update(scenario_net['ip_profile'], ipprofile_db)
             for interface in net_instance_desc.get('interfaces', () ):
                 if 'ip_address' in interface:
                     for vnf in scenarioDict['vnfs']:
@@ -3062,6 +3079,137 @@ def datacenter_new_netmap(mydb, tenant_id, datacenter, action_dict=None):
         net_list.append(net_nfvo)
     return net_list
 
+def get_sdn_net_id(mydb, tenant_id, datacenter, network_id):
+    # obtain all network data
+    try:
+        if utils.check_valid_uuid(network_id):
+            filter_dict = {"id": network_id}
+        else:
+            filter_dict = {"name": network_id}
+
+        datacenter_id, myvim = get_datacenter_by_name_uuid(mydb, tenant_id, datacenter)
+        network = myvim.get_network_list(filter_dict=filter_dict)
+    except vimconn.vimconnException as e:
+        print "vim_action Not possible to get_%s_list from VIM: %s " % (item, str(e))
+        raise NfvoException("Not possible to get_{}_list from VIM: {}".format(item, str(e)), e.http_code)
+
+    # ensure the network is defined
+    if len(network) == 0:
+        raise NfvoException("Network {} is not present in the system".format(network_id),
+                            HTTP_Bad_Request)
+
+    # ensure there is only one network with the provided name
+    if len(network) > 1:
+        raise NfvoException("Multiple networks present in vim identified by {}".format(network_id), HTTP_Bad_Request)
+
+    # ensure it is a dataplane network
+    if network[0]['type'] != 'data':
+        return None
+
+    # ensure we use the id
+    network_id = network[0]['id']
+
+    # search in dabase mano_db in table instance nets for the sdn_net_id that corresponds to the vim_net_id==network_id
+    # and with instance_scenario_id==NULL
+    #search_dict = {'vim_net_id': network_id, 'instance_scenario_id': None}
+    search_dict = {'vim_net_id': network_id}
+
+    try:
+        #sdn_network_id = mydb.get_rows(SELECT=('sdn_net_id',), FROM='instance_nets', WHERE=search_dict)[0]['sdn_net_id']
+        result =  mydb.get_rows(SELECT=('sdn_net_id',), FROM='instance_nets', WHERE=search_dict)
+    except db_base_Exception as e:
+        raise NfvoException("db_base_Exception obtaining SDN network to associated to vim network {}".format(
+            network_id) + str(e), HTTP_Internal_Server_Error)
+
+    sdn_net_counter = 0
+    for net in result:
+        if net['sdn_net_id'] != None:
+            sdn_net_counter+=1
+            sdn_net_id = net['sdn_net_id']
+
+    if sdn_net_counter == 0:
+        return None
+    elif sdn_net_counter == 1:
+        return sdn_net_id
+    else:
+        raise NfvoException("More than one SDN network is associated to vim network {}".format(
+            network_id), HTTP_Internal_Server_Error)
+
+def get_sdn_controller_id(mydb, datacenter):
+    # Obtain sdn controller id
+    config = mydb.get_rows(SELECT=('config',), FROM='datacenters', WHERE={'uuid': datacenter})[0].get('config', '{}')
+    if not config:
+        return None
+
+    return yaml.load(config).get('sdn-controller')
+
+def vim_net_sdn_attach(mydb, tenant_id, datacenter, network_id, descriptor):
+    try:
+        sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, network_id)
+        if not sdn_network_id:
+            raise NfvoException("No SDN network is associated to vim-network {}".format(network_id), HTTP_Internal_Server_Error)
+
+        #Obtain sdn controller id
+        controller_id = get_sdn_controller_id(mydb, datacenter)
+        if not controller_id:
+            raise NfvoException("No SDN controller is set for datacenter {}".format(datacenter), HTTP_Internal_Server_Error)
+
+        #Obtain sdn controller info
+        sdn_controller = ovim.show_of_controller(controller_id)
+
+        port_data = {
+            'name': 'external_port',
+            'net_id': sdn_network_id,
+            'ofc_id': controller_id,
+            'switch_dpid': sdn_controller['dpid'],
+            'switch_port': descriptor['port']
+        }
+
+        if 'vlan' in descriptor:
+            port_data['vlan'] = descriptor['vlan']
+        if 'mac' in descriptor:
+            port_data['mac'] = descriptor['mac']
+
+        result = ovim.new_port(port_data)
+    except ovimException as e:
+        raise NfvoException("ovimException attaching SDN network {} to vim network {}".format(
+            sdn_network_id, network_id) + str(e), HTTP_Internal_Server_Error)
+    except db_base_Exception as e:
+        raise NfvoException("db_base_Exception attaching SDN network to vim network {}".format(
+            network_id) + str(e), HTTP_Internal_Server_Error)
+
+    return 'Port uuid: '+ result
+
+def vim_net_sdn_detach(mydb, tenant_id, datacenter, network_id, port_id=None):
+    if port_id:
+        filter = {'uuid': port_id}
+    else:
+        sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, network_id)
+        if not sdn_network_id:
+            raise NfvoException("No SDN network is associated to vim-network {}".format(network_id),
+                                HTTP_Internal_Server_Error)
+        #in case no port_id is specified only ports marked as 'external_port' will be detached
+        filter = {'name': 'external_port', 'net_id': sdn_network_id}
+
+    try:
+        port_list = ovim.get_ports(columns={'uuid'}, filter=filter)
+    except ovimException as e:
+        raise NfvoException("ovimException obtaining external ports for net {}. ".format(network_id) + str(e),
+                            HTTP_Internal_Server_Error)
+
+    if len(port_list) == 0:
+        raise NfvoException("No ports attached to the network {} were found with the requested criteria".format(network_id),
+                            HTTP_Bad_Request)
+
+    port_uuid_list = []
+    for port in port_list:
+        try:
+            port_uuid_list.append(port['uuid'])
+            ovim.delete_port(port['uuid'])
+        except ovimException as e:
+            raise NfvoException("ovimException deleting port {} for net {}. ".format(port['uuid'], network_id) + str(e), HTTP_Internal_Server_Error)
+
+    return 'Detached ports uuid: {}'.format(','.join(port_uuid_list))
 
 def vim_action_get(mydb, tenant_id, datacenter, item, name):
     #get datacenter info
@@ -3076,9 +3224,32 @@ def vim_action_get(mydb, tenant_id, datacenter, item, name):
         if item=="networks":
             #filter_dict['tenant_id'] = myvim['tenant_id']
             content = myvim.get_network_list(filter_dict=filter_dict)
+
+            if len(content) == 0:
+                raise NfvoException("Network {} is not present in the system. ".format(name),
+                                    HTTP_Bad_Request)
+
+            #Update the networks with the attached ports
+            for net in content:
+                sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, net['id'])
+                if sdn_network_id != None:
+                    try:
+                        #port_list = ovim.get_ports(columns={'uuid', 'switch_port', 'vlan'}, filter={'name': 'external_port', 'net_id': sdn_network_id})
+                        port_list = ovim.get_ports(columns={'uuid', 'switch_port', 'vlan','name'}, filter={'net_id': sdn_network_id})
+                    except ovimException as e:
+                        raise NfvoException("ovimException obtaining external ports for net {}. ".format(network_id) + str(e), HTTP_Internal_Server_Error)
+                    #Remove field name and if port name is external_port save it as 'type'
+                    for port in port_list:
+                        if port['name'] == 'external_port':
+                            port['type'] = "External"
+                        del port['name']
+                    net['sdn_network_id'] = sdn_network_id
+                    net['sdn_attached_ports'] = port_list
+
         elif item=="tenants":
             content = myvim.get_tenant_list(filter_dict=filter_dict)
         elif item == "images":
+
             content = myvim.get_image_list(filter_dict=filter_dict)
         else:
             raise NfvoException(item + "?", HTTP_Method_Not_Allowed)
@@ -3115,6 +3286,36 @@ def vim_action_delete(mydb, tenant_id, datacenter, item, name):
 
     try:
         if item=="networks":
+            # If there is a SDN network associated to the vim-network, proceed to clear the relationship and delete it
+            sdn_network_id = get_sdn_net_id(mydb, tenant_id, datacenter, item_id)
+            if sdn_network_id != None:
+                #Delete any port attachment to this network
+                try:
+                    port_list = ovim.get_ports(columns={'uuid'}, filter={'net_id': sdn_network_id})
+                except ovimException as e:
+                    raise NfvoException(
+                        "ovimException obtaining external ports for net {}. ".format(network_id) + str(e),
+                        HTTP_Internal_Server_Error)
+
+                # By calling one by one all ports to be detached we ensure that not only the external_ports get detached
+                for port in port_list:
+                    vim_net_sdn_detach(mydb, tenant_id, datacenter, item_id, port['uuid'])
+
+                #Delete from 'instance_nets' the correspondence between the vim-net-id and the sdn-net-id
+                try:
+                    mydb.delete_row(FROM='instance_nets', WHERE={'instance_scenario_id': None, 'sdn_net_id': sdn_network_id, 'vim_net_id': item_id})
+                except db_base_Exception as e:
+                    raise NfvoException("Error deleting correspondence for VIM/SDN dataplane networks{}: ".format(correspondence) +
+                                        str(e), HTTP_Internal_Server_Error)
+
+                #Delete the SDN network
+                try:
+                    ovim.delete_network(sdn_network_id)
+                except ovimException as e:
+                    logger.error("ovimException deleting SDN network={} ".format(sdn_network_id) + str(e), exc_info=True)
+                    raise NfvoException("ovimException deleting SDN network={} ".format(sdn_network_id) + str(e),
+                                        HTTP_Internal_Server_Error)
+
             content = myvim.delete_network(item_id)
         elif item=="tenants":
             content = myvim.delete_tenant(item_id)
@@ -3144,6 +3345,32 @@ def vim_action_create(mydb, tenant_id, datacenter, item, descriptor):
             net_ipprofile = net.pop("ip_profile", None)
             net_vlan = net.pop("vlan", None)
             content = myvim.new_network(net_name, net_type, net_ipprofile, shared=net_public, vlan=net_vlan) #, **net)
+
+            #If the datacenter has a SDN controller defined and the network is of dataplane type, then create the sdn network
+            if get_sdn_controller_id(mydb, datacenter) != None and (net_type == 'data' or net_type == 'ptp'):
+                try:
+                    sdn_network = {}
+                    sdn_network['vlan'] = net_vlan
+                    sdn_network['type'] = net_type
+                    sdn_network['name'] = net_name
+                    ovim_content = ovim.new_network(sdn_network)
+                except ovimException as e:
+                    self.logger.error("ovimException creating SDN network={} ".format(
+                        sdn_network) + str(e), exc_info=True)
+                    raise NfvoException("ovimException creating SDN network={} ".format(sdn_network) + str(e),
+                                        HTTP_Internal_Server_Error)
+
+                # Save entry in in dabase mano_db in table instance_nets to stablish a dictionary  vim_net_id <->sdn_net_id
+                # use instance_scenario_id=None to distinguish from real instaces of nets
+                correspondence = {'instance_scenario_id': None, 'sdn_net_id': ovim_content, 'vim_net_id': content}
+                #obtain datacenter_tenant_id
+                correspondence['datacenter_tenant_id'] = mydb.get_rows(SELECT=('uuid',), FROM='datacenter_tenants', WHERE={'datacenter_id': datacenter})[0]['uuid']
+
+                try:
+                    mydb.new_row('instance_nets', correspondence, add_uuid=True)
+                except db_base_Exception as e:
+                    raise NfvoException("Error saving correspondence for VIM/SDN dataplane networks{}: ".format(correspondence) +
+                                        str(e), HTTP_Internal_Server_Error)
         elif item=="tenants":
             tenant = descriptor["tenant"]
             content = myvim.new_tenant(tenant["name"], tenant.get("description"))
@@ -3237,9 +3464,11 @@ def datacenter_sdn_port_mapping_list(mydb, tenant_id, datacenter_id):
             result["sdn-controller"] = controller_id
             result["dpid"] = sdn_controller["dpid"]
 
-    if result["sdn-controller"] == None or result["dpid"] == None:
-        raise NfvoException("Not all SDN controller information for datacenter {} could be found: {}".format(datacenter_id, result),
-                            HTTP_Internal_Server_Error)
+    if result["sdn-controller"] == None:
+        raise NfvoException("SDN controller is not defined for datacenter {}".format(datacenter_id), HTTP_Bad_Request)
+    if result["dpid"] == None:
+        raise NfvoException("It was not possible to determine DPID for SDN controller {}".format(result["sdn-controller"]),
+                        HTTP_Internal_Server_Error)
 
     if len(maps) == 0:
         return result
index ea6d339..ac392c6 100644 (file)
@@ -107,7 +107,7 @@ class nfvo_db(db_base.db_base):
                                 created_time += 0.00001
                                 db_base._convert_bandwidth(dataiface, logger=self.logger)
                                 dataifacesDict[vm['name']][dataiface['name']] = {}
-                                dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface['vpci']
+                                dataifacesDict[vm['name']][dataiface['name']]['vpci'] = dataiface.get('vpci')
                                 dataifacesDict[vm['name']][dataiface['name']]['bw'] = dataiface['bandwidth']
                                 dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface[
                                                                                                      'dedicated'] == "yes" else (
@@ -258,7 +258,7 @@ class nfvo_db(db_base.db_base):
                                 created_time += 0.00001
                                 db_base._convert_bandwidth(dataiface, logger=self.logger)
                                 ifaceDict = {}
-                                ifaceDict['vpci'] = dataiface['vpci']
+                                ifaceDict['vpci'] = dataiface.get('vpci')
                                 ifaceDict['bw'] = dataiface['bandwidth']
                                 ifaceDict['model'] = "PF" if dataiface['dedicated'] == "yes" else \
                                     ("VF" if dataiface['dedicated'] == "no" else "VFnotShared")
index 1dfcffe..7d218ec 100644 (file)
@@ -316,10 +316,11 @@ ip_profile_schema = {
     "$schema": "http://json-schema.org/draft-04/schema#",
     "type":"object",
     "properties":{
-        "ip-version": {"type":"string", "enum":["IPv4","IPv6"]},
+        "ip-version": {"type": "string", "enum": ["IPv4","IPv6"]},
         "subnet-address": ip_prefix_schema,
         "gateway-address": ip_schema,
-        "dns-address": ip_schema,
+        "dns-address": {"oneOf": [ip_schema,     # for backward compatibility
+                                  {"type": "array", "items": ip_schema}]},
         "dhcp": dhcp_schema
     },
 }
@@ -1104,7 +1105,7 @@ instance_scenario_action_schema = {
 
 sdn_controller_properties={
     "name": name_schema,
-    "dpid": {"type":"string", "pattern":"^[0-9a-fA-F][02468aceACE](:[0-9a-fA-F]{2}){7}$"},
+    "dpid": {"type":"string", "pattern":"^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){7}$"},
     "ip": ip_schema,
     "port": port_schema,
     "type": {"type": "string", "enum": ["opendaylight","floodlight","onos"]},
@@ -1172,4 +1173,16 @@ sdn_port_mapping_schema  = {
         }
     },
     "required": ["sdn_port_mapping"]
+}
+
+sdn_external_port_schema = {
+    "$schema": "http://json-schema.org/draft-04/schema#",
+    "title":"External port ingformation",
+    "type": "object",
+    "properties": {
+        "port": {"type" : "string", "minLength":1, "maxLength":60},
+        "vlan": vlan_schema,
+        "mac": mac_schema
+    },
+    "required": ["port"]
 }
\ No newline at end of file
index 373fe7b..9f396a2 100644 (file)
@@ -46,6 +46,8 @@ def is_task_id(task_id):
 
 
 class vim_thread(threading.Thread):
+    REFRESH_BUILD = 5      # 5 seconds
+    REFRESH_ACTIVE = 60    # 1 minute
 
     def __init__(self, vimconn, task_lock, name=None, datacenter_name=None, datacenter_tenant_id=None,
                  db=None, db_lock=None, ovim=None):
@@ -161,7 +163,7 @@ class vim_thread(threading.Thread):
                                     FROM="instance_interfaces as ii left join instance_nets as ine on "
                                          "ii.instance_net_id=ine.uuid left join instance_vms as iv on "
                                          "ii.instance_vm_id=iv.uuid",
-                                    SELECT=("ii.uuid as iface_id", "ine.uuid as net_id", "iv.uuid as vm_id", "sdn_net_id"),
+                                    SELECT=("ii.uuid as iface_id", "ine.uuid as net_id", "iv.uuid as vm_id", "sdn_net_id", "vim_net_id"),
                                     WHERE=where_)
                             if len(db_ifaces)>1:
                                 self.logger.critical("Refresing interfaces. "
@@ -172,6 +174,14 @@ class vim_thread(threading.Thread):
                                 continue
                             else:
                                 db_iface = db_ifaces[0]
+                                #If there is no sdn_net_id, check if it is because an already created vim network is being used
+                                #in that case, the sdn_net_id will be in that entry of the instance_nets table
+                                if not db_iface.get("sdn_net_id"):
+                                    result = self.db.get_rows(SELECT=('sdn_net_id',), FROM='instance_nets',
+                                                                  WHERE={'vim_net_id': db_iface.get("vim_net_id"), 'instance_scenario_id': None, "datacenter_tenant_id":  self.datacenter_tenant_id})
+                                    if len(result) == 1:
+                                        db_iface["sdn_net_id"] = result[0]['sdn_net_id']
+
                                 if db_iface.get("sdn_net_id") and interface.get("compute_node") and interface.get("pci"):
                                     sdn_net_id = db_iface["sdn_net_id"]
                                     sdn_port_name = sdn_net_id + "." + db_iface["vm_id"]
@@ -204,12 +214,12 @@ class vim_thread(threading.Thread):
 
                     task["vim_info"] = vim_info
                     if task["vim_info"]["status"] == "BUILD":
-                        self._insert_refresh(task, now+5)  # 5seconds
+                        self._insert_refresh(task, now + self.REFRESH_BUILD)
                     else:
-                        self._insert_refresh(task, now+300) # 5minutes
+                        self._insert_refresh(task, now + self.REFRESH_ACTIVE)
             except vimconn.vimconnException as e:
                 self.logger.error("vimconnException Exception when trying to refresh vms " + str(e))
-                self._insert_refresh(task, now + 300)  # 5minutes
+                self._insert_refresh(task, now + self.REFRESH_ACTIVE)
 
         if net_to_refresh_list:
             try:
@@ -271,12 +281,12 @@ class vim_thread(threading.Thread):
 
                     task["vim_info"] = vim_info
                     if task["vim_info"]["status"] == "BUILD":
-                        self._insert_refresh(task, now+5)    # 5seconds
+                        self._insert_refresh(task, now + self.REFRESH_BUILD)
                     else:
-                        self._insert_refresh(task, now+300)  # 5minutes
+                        self._insert_refresh(task, now + self.REFRESH_ACTIVE)
             except vimconn.vimconnException as e:
                 self.logger.error("vimconnException Exception when trying to refresh nets " + str(e))
-                self._insert_refresh(task, now + 300)  # 5minutes
+                self._insert_refresh(task, now + self.REFRESH_ACTIVE)
 
         if not items_to_refresh:
             time.sleep(1)
@@ -402,7 +412,7 @@ class vim_thread(threading.Thread):
             sdn_net_id = None
             sdn_controller = self.vim.config.get('sdn-controller')
             if sdn_controller and (net_type == "data" or net_type == "ptp"):
-                network = {"name": net_name, "type": net_type}
+                network = {"name": net_name, "type": net_type, "region": self.vim["config"]["datacenter_id"]}
 
                 vim_net = self.vim.get_network(net_id)
                 if vim_net.get('encapsulation') != 'vlan':
@@ -549,6 +559,20 @@ class vim_thread(threading.Thread):
             self._remove_refresh("get-net", net_id)
             result = self.vim.delete_network(net_id)
             if sdn_net_id:
+                #Delete any attached port to this sdn network
+                #At this point, there will be ports associated to this network in case it was manually done using 'openmano vim-net-sdn-attach'
+                try:
+                    port_list = self.ovim.get_ports(columns={'uuid'}, filter={'name': 'external_port', 'net_id': sdn_net_id})
+                except ovimException as e:
+                    raise vimconn.vimconnException(
+                        "ovimException obtaining external ports for net {}. ".format(sdn_net_id) + str(e))
+
+                for port in port_list:
+                    try:
+                        self.ovim.delete_port(port['uuid'])
+                    except ovimException as e:
+                        raise vimconn.vimconnException(
+                            "ovimException deleting port {} for net {}. ".format(port['uuid'], sdn_net_id) + str(e))
                 with self.db_lock:
                     self.ovim.delete_network(sdn_net_id)
             return True, result
index dbe9acb..f0eebb6 100644 (file)
@@ -117,7 +117,7 @@ class vimconnector(vimconn.vimconnector):
                 except IOError as e:
                     raise vimconn.vimconnException("Error reading file '{}': {}".format(flavor_data[1:], e))
             elif isinstance(flavor_data, dict):
-                self.flavor_data = flavor_data
+                self.flavor_info = flavor_data
 
         self.logger = logging.getLogger('openmano.vim.aws')
         if log_level:
index 539c5a4..580ee34 100644 (file)
@@ -36,19 +36,16 @@ import time
 import yaml
 import random
 
-from novaclient import client as nClient_v2, exceptions as nvExceptions
-from novaclient import api_versions
-import keystoneclient.v2_0.client as ksClient_v2
-from novaclient.v2.client import Client as nClient
-import keystoneclient.v3.client as ksClient
+from novaclient import client as nClient, exceptions as nvExceptions
+from keystoneauth1.identity import v2, v3
+from keystoneauth1 import session
 import keystoneclient.exceptions as ksExceptions
-import glanceclient.v2.client as glClient
+from glanceclient import client as glClient
 import glanceclient.client as gl1Client
 import glanceclient.exc as gl1Exceptions
-import cinderclient.v2.client as cClient_v2
+from  cinderclient import client as cClient
 from httplib import HTTPException
-from neutronclient.neutron import client as neClient_v2
-from neutronclient.v2_0 import client as neClient
+from neutronclient.neutron import client as neClient
 from neutronclient.common import exceptions as neExceptions
 from requests.exceptions import ConnectionError
 
@@ -74,137 +71,67 @@ class vimconnector(vimconn.vimconnector):
         'url' is the keystone authorization url,
         'url_admin' is not use
         '''
-        self.osc_api_version = 'v2.0'
-        if config.get('APIversion') == 'v3.3':
-            self.osc_api_version = 'v3.3'
-        vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level, config)
+        self.osc_api_version = config.get('APIversion')
+        if self.osc_api_version != 'v3.3' and self.osc_api_version != 'v2.0' and self.osc_api_version:
+            raise vimconn.vimconnException("Invalid value '{}' for config:APIversion. "
+                "Allowed values are 'v3.3' or 'v2.0'".format(self.osc_api_version))
+        vimconn.vimconnector.__init__(self, uuid, name, tenant_id, tenant_name, url, url_admin, user, passwd, log_level,
+                                      config)
 
-        self.persistent_info = persistent_info
-        self.k_creds={}
-        self.n_creds={}
-        if self.config.get("insecure"):
-            self.k_creds["insecure"] = True
-            self.n_creds["insecure"] = True
+        self.insecure = self.config.get("insecure", False)
         if not url:
             raise TypeError, 'url param can not be NoneType'
-        self.k_creds['auth_url'] = url
-        self.n_creds['auth_url'] = url
-        if tenant_name:
-            self.k_creds['tenant_name'] = tenant_name
-            self.n_creds['project_id']  = tenant_name
-        if tenant_id:
-            self.k_creds['tenant_id'] = tenant_id
-            self.n_creds['tenant_id']  = tenant_id
-        if user:
-            self.k_creds['username'] = user
-            self.n_creds['username'] = user
-        if passwd:
-            self.k_creds['password'] = passwd
-            self.n_creds['api_key']  = passwd
-        if self.osc_api_version == 'v3.3':
-            self.k_creds['project_name'] = tenant_name
-            self.k_creds['project_id'] = tenant_id
-        if config.get('region_name'):
-            self.k_creds['region_name'] = config.get('region_name')
-            self.n_creds['region_name'] = config.get('region_name')
+        self.auth_url = url
+        self.tenant_name = tenant_name
+        self.tenant_id = tenant_id
+        self.user = user
+        self.passwd = passwd
+        self.persistent_info = persistent_info
+        self.session = persistent_info.get('session', {'reload_client': True})
+        self.nova = self.session.get('nova')
+        self.neutron = self.session.get('neutron')
+        self.cinder = self.session.get('cinder')
+        self.glance = self.session.get('glance')
 
-        self.reload_client       = True
         self.logger = logging.getLogger('openmano.vim.openstack')
         if log_level:
             self.logger.setLevel( getattr(logging, log_level) )
     
     def __setitem__(self,index, value):
-        '''Set individuals parameters 
+        '''Set individuals parameters
         Throw TypeError, KeyError
         '''
-        if index=='tenant_id':
-            self.reload_client=True
-            self.tenant_id = value
-            if self.osc_api_version == 'v3.3':
-                if value:
-                    self.k_creds['project_id'] = value
-                    self.n_creds['project_id']  = value
-                else:
-                    del self.k_creds['project_id']
-                    del self.n_creds['project_id']
-            else:
-                if value:
-                    self.k_creds['tenant_id'] = value
-                    self.n_creds['tenant_id']  = value
-                else:
-                    del self.k_creds['tenant_id']
-                    del self.n_creds['tenant_id']
-        elif index=='tenant_name':
-            self.reload_client=True
-            self.tenant_name = value
-            if self.osc_api_version == 'v3.3':
-                if value:
-                    self.k_creds['project_name'] = value
-                    self.n_creds['project_name']  = value
-                else:
-                    del self.k_creds['project_name']
-                    del self.n_creds['project_name']
-            else:
-                if value:
-                    self.k_creds['tenant_name'] = value
-                    self.n_creds['project_id']  = value
-                else:
-                    del self.k_creds['tenant_name']
-                    del self.n_creds['project_id']
-        elif index=='user':
-            self.reload_client=True
-            self.user = value
-            if value:
-                self.k_creds['username'] = value
-                self.n_creds['username'] = value
-            else:
-                del self.k_creds['username']
-                del self.n_creds['username']
-        elif index=='passwd':
-            self.reload_client=True
-            self.passwd = value
-            if value:
-                self.k_creds['password'] = value
-                self.n_creds['api_key']  = value
-            else:
-                del self.k_creds['password']
-                del self.n_creds['api_key']
-        elif index=='url':
-            self.reload_client=True
-            self.url = value
-            if value:
-                self.k_creds['auth_url'] = value
-                self.n_creds['auth_url'] = value
-            else:
-                raise TypeError, 'url param can not be NoneType'
-        else:
-            vimconn.vimconnector.__setitem__(self,index, value)
+        self.session['reload_client'] = True
+        vimconn.vimconnector.__setitem__(self,index, value)
      
     def _reload_connection(self):
         '''Called before any operation, it check if credentials has changed
         Throw keystoneclient.apiclient.exceptions.AuthorizationFailure
         '''
         #TODO control the timing and possible token timeout, but it seams that python client does this task for us :-) 
-        if self.reload_client:
-            #test valid params
-            if len(self.n_creds) <4:
-                raise ksExceptions.ClientException("Not enough parameters to connect to openstack")
-            if self.osc_api_version == 'v3.3':
-                self.nova = nClient(api_version=api_versions.APIVersion(version_str='2.0'), **self.n_creds)
-                #TODO To be updated for v3
-                #self.cinder = cClient.Client(**self.n_creds)
-                self.keystone = ksClient.Client(**self.k_creds)
-                self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
-                self.neutron = neClient.Client(api_version=api_versions.APIVersion(version_str='2.0'), endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
+        if self.session['reload_client']:
+            if self.osc_api_version == 'v3.3' or self.osc_api_version == '3' or \
+                    (not self.osc_api_version and self.auth_url.split("/")[-1] == "v3"):
+                auth = v3.Password(auth_url=self.auth_url,
+                                   username=self.user,
+                                   password=self.passwd,
+                                   project_name=self.tenant_name,
+                                   project_id=self.tenant_id,
+                                   project_domain_id=self.config.get('project_domain_id', 'default'),
+                                   user_domain_id=self.config.get('user_domain_id', 'default'))
             else:
-                self.nova = nClient_v2.Client(version='2', **self.n_creds)
-                self.cinder = cClient_v2.Client(**self.n_creds)
-                self.keystone = ksClient_v2.Client(**self.k_creds)
-                self.ne_endpoint=self.keystone.service_catalog.url_for(service_type='network', endpoint_type='publicURL')
-                self.neutron = neClient_v2.Client('2.0', endpoint_url=self.ne_endpoint, token=self.keystone.auth_token, **self.k_creds)
-            self.glance_endpoint = self.keystone.service_catalog.url_for(service_type='image', endpoint_type='publicURL')
-            self.glance = glClient.Client(self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds)  #TODO check k_creds vs n_creds
-            self.reload_client = False
+                auth = v2.Password(auth_url=self.auth_url,
+                                   username=self.user,
+                                   password=self.passwd,
+                                   tenant_name=self.tenant_name,
+                                   tenant_id=self.tenant_id)
+            sess = session.Session(auth=auth, verify=not self.insecure)
+            self.nova = self.session['nova'] = nClient.Client("2.1", session=sess)
+            self.neutron = self.session['neutron'] = neClient.Client('2.0', session=sess)
+            self.cinder = self.session['cinder'] = cClient.Client(2, session=sess)
+            self.glance = self.session['glance'] = glClient.Client(2, session=sess)
+            self.session['reload_client'] = False
+            self.persistent_info['session'] = self.session
 
     def __net_os2mano(self, net_list_dict):
         '''Transform the net openstack format to mano format
@@ -322,9 +249,7 @@ class vimconnector(vimconn.vimconnector):
             if 'gateway_address' in ip_profile:
                 subnet['gateway_ip'] = ip_profile['gateway_address']
             if ip_profile.get('dns_address'):
-                #TODO: manage dns_address as a list of addresses separated by commas 
-                subnet['dns_nameservers'] = []
-                subnet['dns_nameservers'].append(ip_profile['dns_address'])
+                subnet['dns_nameservers'] = ip_profile['dns_address'].split(";")
             if 'dhcp_enabled' in ip_profile:
                 subnet['enable_dhcp'] = False if ip_profile['dhcp_enabled']=="false" else True
             if 'dhcp_start_address' in ip_profile:
@@ -472,11 +397,19 @@ class vimconnector(vimconn.vimconnector):
     def get_flavor_id_from_data(self, flavor_dict):
         """Obtain flavor id that match the flavor description
            Returns the flavor_id or raises a vimconnNotFoundException
+           flavor_dict: contains the required ram, vcpus, disk
+           If 'use_existing_flavors' is set to True at config, the closer flavor that provides same or more ram, vcpus
+                and disk is returned. Otherwise a flavor with exactly same ram, vcpus and disk is returned or a
+                vimconnNotFoundException is raised
         """
+        exact_match = False if self.config.get('use_existing_flavors') else True
         try:
             self._reload_connection()
-            numa=None
-            numas = flavor_dict.get("extended",{}).get("numas")
+            flavor_candidate_id = None
+            flavor_candidate_data = (10000, 10000, 10000)
+            flavor_target = (flavor_dict["ram"], flavor_dict["vcpus"], flavor_dict["disk"])
+            # numa=None
+            numas = flavor_dict.get("extended", {}).get("numas")
             if numas:
                 #TODO
                 raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemted")
@@ -488,14 +421,15 @@ class vimconnector(vimconn.vimconnector):
                 epa = flavor.get_keys()
                 if epa:
                     continue
-                    #TODO 
-                if flavor.ram != flavor_dict["ram"]:
-                    continue
-                if flavor.vcpus != flavor_dict["vcpus"]:
-                    continue
-                if flavor.disk != flavor_dict["disk"]:
-                    continue
-                return flavor.id
+                    # TODO
+                flavor_data = (flavor.ram, flavor.vcpus, flavor.disk)
+                if flavor_data == flavor_target:
+                    return flavor.id
+                elif not exact_match and flavor_target < flavor_data < flavor_candidate_data:
+                    flavor_candidate_id = flavor.id
+                    flavor_candidate_data = flavor_data
+            if not exact_match and flavor_candidate_id:
+                return flavor_candidate_id
             raise vimconn.vimconnNotFoundException("Cannot find any flavor matching '{}'".format(str(flavor_dict)))
         except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
             self._format_exception(e)
@@ -543,15 +477,22 @@ class vimconnector(vimconn.vimconnector):
                         for numa in numas:
                             #overwrite ram and vcpus
                             ram = numa['memory']*1024
+                            #See for reference: https://specs.openstack.org/openstack/nova-specs/specs/mitaka/implemented/virt-driver-cpu-thread-pinning.html
                             if 'paired-threads' in numa:
                                 vcpus = numa['paired-threads']*2
-                                numa_properties["hw:cpu_threads_policy"] = "prefer"
+                                #cpu_thread_policy "require" implies that the compute node must have an STM architecture
+                                numa_properties["hw:cpu_thread_policy"] = "require"
+                                numa_properties["hw:cpu_policy"] = "dedicated"
                             elif 'cores' in numa:
                                 vcpus = numa['cores']
-                                #numa_properties["hw:cpu_threads_policy"] = "prefer"
+                                # cpu_thread_policy "prefer" implies that the host must not have an SMT architecture, or a non-SMT architecture will be emulated
+                                numa_properties["hw:cpu_thread_policy"] = "isolate"
+                                numa_properties["hw:cpu_policy"] = "dedicated"
                             elif 'threads' in numa:
                                 vcpus = numa['threads']
-                                numa_properties["hw:cpu_policy"] = "isolated"
+                                # cpu_thread_policy "prefer" implies that the host may or may not have an SMT architecture
+                                numa_properties["hw:cpu_thread_policy"] = "prefer"
+                                numa_properties["hw:cpu_policy"] = "dedicated"
                             # for interface in numa.get("interfaces",() ):
                             #     if interface["dedicated"]=="yes":
                             #         raise vimconn.vimconnException("Passthrough interfaces are not supported for the openstack connector", http_code=vimconn.HTTP_Service_Unavailable)
@@ -597,6 +538,7 @@ class vimconnector(vimconn.vimconnector):
             metadata: metadata of the image
         Returns the image_id
         '''
+        # ALF TODO: revise and change for the new method or session
         #using version 1 of glance client
         glancev1 = gl1Client.Client('1',self.glance_endpoint, token=self.keystone.auth_token, **self.k_creds)  #TODO check k_creds vs n_creds
         retry=0
index e86b54d..e722dc8 100644 (file)
@@ -596,6 +596,9 @@ class vimconnector(vimconn.vimconnector):
         '''Returns the flavor identifier'''
         try:
             new_flavor_dict = flavor_data.copy()
+            for device in new_flavor_dict.get('extended', {}).get('devices', ()):
+                if 'image name' in device:
+                    del device['image name']
             new_flavor_dict["name"] = flavor_data["name"][:64]
             self._get_my_tenant()
             payload_req = json.dumps({'flavor': new_flavor_dict})
index a56054a..a33ca4e 100644 (file)
@@ -1444,7 +1444,12 @@ class vimconnector(vimconn.vimconnector):
         if disk_list:
             added_existing_disk = False
             for disk in disk_list:
-                if "image_id" in disk and disk["image_id"] is not None:
+                if 'device_type' in disk and disk['device_type'] == 'cdrom':
+                    image_id = disk['image_id']
+                    # Adding CD-ROM to VM
+                    # will revisit code once specification ready to support this feature
+                    self.insert_media_to_vm(vapp, image_id)
+                elif "image_id" in disk and disk["image_id"] is not None:
                     self.logger.debug("Adding existing disk from image {} to vm {} ".format(
                                                                     disk["image_id"] , vapp_uuid))
                     self.add_existing_disk(catalogs=catalogs,
@@ -2746,6 +2751,11 @@ class vimconnector(vimconn.vimconnector):
             #Unused in case of Underlay (data/ptp) network interface.
             fence_mode="bridged"
             is_inherited='false'
+            dns_list = dns_address.split(";")
+            dns1 = dns_list[0]
+            dns2_text = ""
+            if len(dns_list) >= 2:
+                dns2_text = "\n                                                <Dns2>{}</Dns2>\n".format(dns_list[1])
             data = """ <OrgVdcNetwork name="{0:s}" xmlns="http://www.vmware.com/vcloud/v1.5">
                             <Description>Openmano created</Description>
                                     <Configuration>
@@ -2754,22 +2764,22 @@ class vimconnector(vimconn.vimconnector):
                                                 <IsInherited>{1:s}</IsInherited>
                                                 <Gateway>{2:s}</Gateway>
                                                 <Netmask>{3:s}</Netmask>
-                                                <Dns1>{4:s}</Dns1>
-                                                <IsEnabled>{5:s}</IsEnabled>
+                                                <Dns1>{4:s}</Dns1>{5:s}
+                                                <IsEnabled>{6:s}</IsEnabled>
                                                 <IpRanges>
                                                     <IpRange>
-                                                        <StartAddress>{6:s}</StartAddress>
-                                                        <EndAddress>{7:s}</EndAddress>
+                                                        <StartAddress>{7:s}</StartAddress>
+                                                        <EndAddress>{8:s}</EndAddress>
                                                     </IpRange>
                                                 </IpRanges>
                                             </IpScope>
                                         </IpScopes>
-                                        <ParentNetwork href="{8:s}"/>
-                                        <FenceMode>{9:s}</FenceMode>
+                                        <ParentNetwork href="{9:s}"/>
+                                        <FenceMode>{10:s}</FenceMode>
                                     </Configuration>
-                                    <IsShared>{10:s}</IsShared>
+                                    <IsShared>{11:s}</IsShared>
                         </OrgVdcNetwork> """.format(escape(network_name), is_inherited, gateway_address,
-                                                    subnet_address, dns_address, dhcp_enabled,
+                                                    subnet_address, dns1, dns2_text, dhcp_enabled,
                                                     dhcp_start_address, dhcp_end_address, available_networks,
                                                     fence_mode, isshared)
 
@@ -4788,3 +4798,114 @@ class vimconnector(vimconn.vimconnector):
                 break
         return obj
 
+
+    def insert_media_to_vm(self, vapp, image_id):
+        """
+        Method to insert media CD-ROM (ISO image) from catalog to vm.
+        vapp - vapp object to get vm id
+        Image_id - image id for cdrom to be inerted to vm
+        """
+        # create connection object
+        vca = self.connect()
+        try:
+            # fetching catalog details
+            rest_url = "{}/api/catalog/{}".format(vca.host,image_id)
+            response = Http.get(url=rest_url,
+                                headers=vca.vcloud_session.get_vcloud_headers(),
+                                verify=vca.verify,
+                                logger=vca.logger)
+
+            if response.status_code != 200:
+                self.logger.error("REST call {} failed reason : {}"\
+                             "status code : {}".format(url_rest_call,
+                                                    response.content,
+                                               response.status_code))
+                raise vimconn.vimconnException("insert_media_to_vm(): Failed to get "\
+                                                                    "catalog details")
+            # searching iso name and id
+            iso_name,media_id = self.get_media_details(vca, response.content)
+
+            if iso_name and media_id:
+                data ="""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+                     <ns6:MediaInsertOrEjectParams
+                     xmlns="http://www.vmware.com/vcloud/versions" xmlns:ns2="http://schemas.dmtf.org/ovf/envelope/1" xmlns:ns3="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_VirtualSystemSettingData" xmlns:ns4="http://schemas.dmtf.org/wbem/wscim/1/common" xmlns:ns5="http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData" xmlns:ns6="http://www.vmware.com/vcloud/v1.5" xmlns:ns7="http://www.vmware.com/schema/ovf" xmlns:ns8="http://schemas.dmtf.org/ovf/environment/1" xmlns:ns9="http://www.vmware.com/vcloud/extension/v1.5">
+                     <ns6:Media
+                        type="application/vnd.vmware.vcloud.media+xml"
+                        name="{}.iso"
+                        id="urn:vcloud:media:{}"
+                        href="https://{}/api/media/{}"/>
+                     </ns6:MediaInsertOrEjectParams>""".format(iso_name, media_id,
+                                                                vca.host,media_id)
+
+                for vms in vapp._get_vms():
+                    vm_id = (vms.id).split(':')[-1]
+
+                    headers = vca.vcloud_session.get_vcloud_headers()
+                    headers['Content-Type'] = 'application/vnd.vmware.vcloud.mediaInsertOrEjectParams+xml'
+                    rest_url = "{}/api/vApp/vm-{}/media/action/insertMedia".format(vca.host,vm_id)
+
+                    response = Http.post(url=rest_url,
+                                      headers=headers,
+                                            data=data,
+                                    verify=vca.verify,
+                                    logger=vca.logger)
+
+                    if response.status_code != 202:
+                        self.logger.error("Failed to insert CD-ROM to vm")
+                        raise vimconn.vimconnException("insert_media_to_vm() : Failed to insert"\
+                                                                                    "ISO image to vm")
+                    else:
+                        task = taskType.parseString(response.content, True)
+                        if isinstance(task, GenericTask):
+                            vca.block_until_completed(task)
+                            self.logger.info("insert_media_to_vm(): Sucessfully inserted media ISO"\
+                                                                    " image to vm {}".format(vm_id))
+        except Exception as exp:
+            self.logger.error("insert_media_to_vm() : exception occurred "\
+                                            "while inserting media CD-ROM")
+            raise vimconn.vimconnException(message=exp)
+
+
+    def get_media_details(self, vca, content):
+        """
+        Method to get catalog item details
+        vca - connection object
+        content - Catalog details
+        Return - Media name, media id
+        """
+        cataloghref_list = []
+        try:
+            if content:
+                vm_list_xmlroot = XmlElementTree.fromstring(content)
+                for child in vm_list_xmlroot.iter():
+                    if 'CatalogItem' in child.tag:
+                        cataloghref_list.append(child.attrib.get('href'))
+                if cataloghref_list is not None:
+                    for href in cataloghref_list:
+                        if href:
+                            response = Http.get(url=href,
+                                        headers=vca.vcloud_session.get_vcloud_headers(),
+                                        verify=vca.verify,
+                                        logger=vca.logger)
+                            if response.status_code != 200:
+                                self.logger.error("REST call {} failed reason : {}"\
+                                             "status code : {}".format(href,
+                                                           response.content,
+                                                      response.status_code))
+                                raise vimconn.vimconnException("get_media_details : Failed to get "\
+                                                                         "catalogitem details")
+                            list_xmlroot = XmlElementTree.fromstring(response.content)
+                            for child in list_xmlroot.iter():
+                                if 'Entity' in child.tag:
+                                    if 'media' in child.attrib.get('href'):
+                                        name = child.attrib.get('name')
+                                        media_id = child.attrib.get('href').split('/').pop()
+                                        return name,media_id
+                            else:
+                                self.logger.debug("Media name and id not found")
+                                return False,False
+        except Exception as exp:
+            self.logger.error("get_media_details : exception occurred "\
+                                               "getting media details")
+            raise vimconn.vimconnException(message=exp)
+
index eb92478..0d6c432 100644 (file)
@@ -6,6 +6,7 @@ paramiko
 argcomplete
 requests
 logutils
+python-openstackclient
 python-novaclient
 python-keystoneclient
 python-glanceclient
index 0110d75..624dce3 100755 (executable)
@@ -249,8 +249,11 @@ then
     [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-boto"  #TODO check if at Centos it exists with this name, or PIP should be used
 
     # install openstack client needed for using openstack as a VIM
-    [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient"
-    [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-devel" && easy_install python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient #TODO revise if gcc python-pip is needed
+    [ "$_DISTRO" == "Ubuntu" ] && install_packages "python-novaclient python-keystoneclient python-glanceclient "\
+                                                   "python-neutronclient python-cinderclient python-openstackclient"
+    [ "$_DISTRO" == "CentOS" -o "$_DISTRO" == "Red" ] && install_packages "python-devel" && easy_install \
+        python-novaclient python-keystoneclient python-glanceclient python-neutronclient python-cinderclient \
+        python-openstackclient #TODO revise if gcc python-pip is needed
 fi  # [[ -z "$NO_PACKAGES" ]]
 
 if [[ -z $NOCLONE ]]; then
@@ -270,8 +273,9 @@ if [[ -z $NOCLONE ]]; then
         fi
     fi
     su $SUDO_USER -c "git clone ${GIT_URL} ${BASEFOLDER}"
+    LATEST_STABLE_TAG=`git -C "${BASEFOLDER}" tag -l v[0-9].* | tail -n1`
+    [[ -z $DEVELOP ]] && su $SUDO_USER -c "git -C ${BASEFOLDER} checkout tags/${LATEST_STABLE_TAG}"
     su $SUDO_USER -c "cp ${BASEFOLDER}/.gitignore-common ${BASEFOLDER}/.gitignore"
-    [[ -z $DEVELOP ]] && su $SUDO_USER -c "git -C ${BASEFOLDER} checkout v2.0"
 fi
 
 echo -e "\n"\
index 5f1382d..303a652 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -24,6 +24,7 @@ _requirements = [
     "argcomplete",
     "requests",
     "logutils",
+    "python-openstackclient",
     "python-novaclient",
     "python-keystoneclient",
     "python-glanceclient",
index 7fc8a8e..a0c20ec 100644 (file)
--- a/stdeb.cfg
+++ b/stdeb.cfg
@@ -2,5 +2,5 @@
 Suite: xenial
 XS-Python-Version: >= 2.7
 Maintainer: Gerardo Garcia <gerardo.garciadeblas@telefonica.com>
-Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-novaclient, python-mysqldb
+Depends: python-pip, libmysqlclient-dev, libssl-dev, libffi-dev, python-argcomplete, python-boto, python-bottle, python-jsonschema, python-logutils, python-cinderclient, python-glanceclient, python-keystoneclient, python-neutronclient, python-novaclient, python-openstackclient, python-mysqldb
 
index e38b208..3af91a7 100755 (executable)
@@ -35,6 +35,7 @@ function usage(){
     echo -e "    del-openvim detaches and deletes the local openvim datacenter"
     echo -e "    create      creates VNFs, scenarios and instances"
     echo -e "    delete      deletes the created instances, scenarios and VNFs"
+    echo -e "    delete-all  deletes ALL the existing instances, scenarios and vnf at the current tenant"
     echo -e "  OPTIONS:"
     echo -e "    -f --force       does not prompt for confirmation"
     echo -e "    -h --help        shows this help"
@@ -84,7 +85,8 @@ action_list=""
 for argument in $params
 do
     if [[ $argument == reset ]] || [[ $argument == create ]] || [[ $argument == delete ]] ||
-       [[ $argument == add-openvim ]] || [[ $argument == del-openvim ]] || [[ -z "$argument" ]]
+       [[ $argument == add-openvim ]] || [[ $argument == del-openvim ]] ||  [[ $argument == delete-all ]] ||
+       [[ -z "$argument" ]]
     then
         action_list="$action_list $argument"
         continue
@@ -195,9 +197,20 @@ then
     $openmano vnf-delete -f linux_2VMs_v02        || echo "fail"
     $openmano vnf-delete -f dataplaneVNF_2VMs     || echo "fail"
     $openmano vnf-delete -f dataplaneVNF_2VMs_v02 || echo "fail"
-    $openmano vnf-delete -f dataplaneVNF4         || echo "fail"
+    $openmano vnf-delete -f dataplaneVNF1         || echo "fail"
     $openmano vnf-delete -f dataplaneVNF2         || echo "fail"
     $openmano vnf-delete -f dataplaneVNF3         || echo "fail"
+    $openmano vnf-delete -f dataplaneVNF4         || echo "fail"
+
+elif [[ $action == "delete-all" ]]
+then
+    for i in instance-scenario scenario vnf
+    do
+        for f in `$openmano $i-list | awk '{print $1}'`
+        do
+            [[ -n "$f" ]] && [[ "$f" != No ]] && $openmano ${i}-delete -f ${f}
+        done
+    done
 
 elif [[ $action == "del-openvim" ]]
 then
index 26a7c94..0d5e624 100755 (executable)
 '''
 Module for testing openmano functionality. It uses openmanoclient.py for invoking openmano
 '''
-__author__="Pablo Montes, Alfonso Tierno"
-__date__ ="$16-Feb-2017 17:08:16$"
-__version__="0.0.3"
-version_date="May 2017"
+__author__ = "Pablo Montes, Alfonso Tierno"
+__date__ = "$16-Feb-2017 17:08:16$"
+__version__ = "0.0.4"
+version_date = "Jun 2017"
 
 import logging
 import os
@@ -41,10 +41,32 @@ import glob
 import yaml
 import sys
 import time
+from pyvcloud.vcloudair import VCA
+import uuid
 
 global test_config   #  used for global variables with the test configuration
 test_config = {}
 
+class test_base(unittest.TestCase):
+    test_index = 1
+    test_text = None
+
+    @classmethod
+    def setUpClass(cls):
+        logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+
+    @classmethod
+    def tearDownClass(cls):
+        test_config["test_number"] += 1
+
+    def tearDown(self):
+        exec_info = sys.exc_info()
+        if exec_info == (None, None, None):
+            logger.info(self.__class__.test_text+" -> TEST OK")
+        else:
+            logger.warning(self.__class__.test_text+" -> TEST NOK")
+            logger.critical("Traceback error",exc_info=True)
+
 
 def check_instance_scenario_active(uuid):
     instance = test_config["client"].get_instance(uuid=uuid)
@@ -67,30 +89,8 @@ def check_instance_scenario_active(uuid):
 IMPORTANT NOTE
 All unittest classes for code based tests must have prefix 'test_' in order to be taken into account for tests
 '''
-class test_VIM_datacenter_tenant_operations(unittest.TestCase):
-    test_index = 1
+class test_VIM_datacenter_tenant_operations(test_base):
     tenant_name = None
-    test_text = None
-
-    @classmethod
-    def setUpClass(cls):
-        logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
-
-    @classmethod
-    def tearDownClass(cls):
-        test_config["test_number"] += 1
-
-    def tearDown(self):
-        exec_info = sys.exc_info()
-        if exec_info == (None, None, None):
-            logger.info(self.__class__.test_text+" -> TEST OK")
-        else:
-            logger.warning(self.__class__.test_text+" -> TEST NOK")
-            error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2])
-            msg = ""
-            for line in error_trace:
-                msg = msg + line
-            logger.critical("{}".format(msg))
 
     def test_000_create_RO_tenant(self):
         self.__class__.tenant_name = _get_random_string(20)
@@ -119,30 +119,8 @@ class test_VIM_datacenter_tenant_operations(unittest.TestCase):
         assert('deleted' in tenant.get('result',""))
 
 
-class test_VIM_datacenter_operations(unittest.TestCase):
-    test_index = 1
+class test_VIM_datacenter_operations(test_base):
     datacenter_name = None
-    test_text = None
-
-    @classmethod
-    def setUpClass(cls):
-        logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
-
-    @classmethod
-    def tearDownClass(cls):
-        test_config["test_number"] += 1
-
-    def tearDown(self):
-        exec_info = sys.exc_info()
-        if exec_info == (None, None, None):
-            logger.info(self.__class__.test_text+" -> TEST OK")
-        else:
-            logger.warning(self.__class__.test_text+" -> TEST NOK")
-            error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2])
-            msg = ""
-            for line in error_trace:
-                msg = msg + line
-            logger.critical("{}".format(msg))
 
     def test_000_create_datacenter(self):
         self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index,
@@ -201,32 +179,10 @@ class test_VIM_datacenter_operations(unittest.TestCase):
         assert('deleted' in self.datacenter.get('result',""))
 
 
-class test_VIM_network_operations(unittest.TestCase):
-    test_index = 1
+class test_VIM_network_operations(test_base):
     vim_network_name = None
-    test_text = None
     vim_network_uuid = None
 
-    @classmethod
-    def setUpClass(cls):
-        logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
-
-    @classmethod
-    def tearDownClass(cls):
-        test_config["test_number"] += 1
-
-    def tearDown(self):
-        exec_info = sys.exc_info()
-        if exec_info == (None, None, None):
-            logger.info(self.__class__.test_text + " -> TEST OK")
-        else:
-            logger.warning(self.__class__.test_text + " -> TEST NOK")
-            error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2])
-            msg = ""
-            for line in error_trace:
-                msg = msg + line
-            logger.critical("{}".format(msg))
-
     def test_000_create_VIM_network(self):
         self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index,
                                                            inspect.currentframe().f_code.co_name)
@@ -263,29 +219,7 @@ class test_VIM_network_operations(unittest.TestCase):
         assert ('deleted' in network.get('result', ""))
 
 
-class test_VIM_image_operations(unittest.TestCase):
-    test_index = 1
-    test_text = None
-
-    @classmethod
-    def setUpClass(cls):
-        logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
-
-    @classmethod
-    def tearDownClass(cls):
-        test_config["test_number"] += 1
-
-    def tearDown(self):
-        exec_info = sys.exc_info()
-        if exec_info == (None, None, None):
-            logger.info(self.__class__.test_text + " -> TEST OK")
-        else:
-            logger.warning(self.__class__.test_text + " -> TEST NOK")
-            error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2])
-            msg = ""
-            for line in error_trace:
-                msg = msg + line
-            logger.critical("{}".format(msg))
+class test_VIM_image_operations(test_base):
 
     def test_000_list_VIM_images(self):
         self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index,
@@ -299,34 +233,16 @@ The following is a non critical test that will fail most of the times.
 In case of OpenStack datacenter these tests will only success if RO has access to the admin endpoint
 This test will only be executed in case it is specifically requested by the user
 '''
-class test_VIM_tenant_operations(unittest.TestCase):
-    test_index = 1
+class test_VIM_tenant_operations(test_base):
     vim_tenant_name = None
-    test_text = None
     vim_tenant_uuid = None
 
     @classmethod
     def setUpClass(cls):
-        logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+        test_base.setUpClass(cls)
         logger.warning("In case of OpenStack datacenter these tests will only success "
                        "if RO has access to the admin endpoint")
 
-    @classmethod
-    def tearDownClass(cls):
-        test_config["test_number"] += 1
-
-    def tearDown(self):
-        exec_info = sys.exc_info()
-        if exec_info == (None, None, None):
-            logger.info(self.__class__.test_text + " -> TEST OK")
-        else:
-            logger.warning(self.__class__.test_text + " -> TEST NOK")
-            error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2])
-            msg = ""
-            for line in error_trace:
-                msg = msg + line
-            logger.critical("{}".format(msg))
-
     def test_000_create_VIM_tenant(self):
         self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"], self.__class__.test_index,
                                                            inspect.currentframe().f_code.co_name)
@@ -362,14 +278,561 @@ class test_VIM_tenant_operations(unittest.TestCase):
         logger.debug("{}".format(tenant))
         assert ('deleted' in tenant.get('result', ""))
 
+class test_vimconn_connect(test_base):
+    # test_index = 1
+    # test_text = None
+
+    # @classmethod
+    # def setUpClass(cls):
+    #     logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+
+    # @classmethod
+    # def tearDownClass(cls):
+    #     test_config["test_number"] += 1
+
+    # def tearDown(self):
+    #     exec_info = sys.exc_info()
+    #     if exec_info == (None, None, None):
+    #         logger.info(self.__class__.test_text+" -> TEST OK")
+    #     else:
+    #         logger.warning(self.__class__.test_text+" -> TEST NOK")
+    #         logger.critical("Traceback error",exc_info=True)
+
+    def test_000_connect(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+
+        self.__class__.test_index += 1
+        if test_config['vimtype'] == 'vmware':
+            vca_object = test_config["vim_conn"].connect()
+            logger.debug("{}".format(vca_object))
+            self.assertIsInstance(vca_object, VCA)
+
+
+class test_vimconn_new_network(test_base):
+    # test_index = 1
+    network_name = None
+    # test_text = None
+
+    # @classmethod
+    # def setUpClass(cls):
+    #     logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+
+    # @classmethod
+    # def tearDownClass(cls):
+    #     test_config["test_number"] += 1
+
+    # def tearDown(self):
+    #     exec_info = sys.exc_info()
+    #     if exec_info == (None, None, None):
+    #         logger.info(self.__class__.test_text+" -> TEST OK")
+    #     else:
+    #         logger.warning(self.__class__.test_text+" -> TEST NOK")
+    #         logger.critical("Traceback error",exc_info=True)
+
+    def test_000_new_network(self):
+        self.__class__.network_name = _get_random_string(20)
+        network_type = 'bridge'
+
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                     self.__class__.test_index, inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                          net_type=network_type)
+        self.__class__.network_id = network
+        logger.debug("{}".format(network))
+
+        network_list = test_config["vim_conn"].get_vcd_network_list()
+        for net in network_list:
+            if self.__class__.network_name in net.get('name'):
+                self.assertIn(self.__class__.network_name, net.get('name'))
+                self.assertEqual(net.get('type'), network_type)
+
+        # Deleting created network
+        result = test_config["vim_conn"].delete_network(self.__class__.network_id)
+        if result:
+            logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id))
+        else:
+            logger.info("Failed to delete network id {}".format(self.__class__.network_id))
+
+    def test_010_new_network_by_types(self):
+        delete_net_ids = []
+        network_types = ['data','bridge','mgmt']
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+        for net_type in network_types:
+            self.__class__.network_name = _get_random_string(20)
+            network_id = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                                                net_type=net_type)
+
+            delete_net_ids.append(network_id)
+            logger.debug("{}".format(network_id))
+
+            network_list = test_config["vim_conn"].get_vcd_network_list()
+            for net in network_list:
+                if self.__class__.network_name in net.get('name'):
+                    self.assertIn(self.__class__.network_name, net.get('name'))
+                if net_type in net.get('type'):
+                    self.assertEqual(net.get('type'), net_type)
+                else:
+                    self.assertNotEqual(net.get('type'), net_type)
+
+        # Deleting created network
+        for net_id in delete_net_ids:
+            result = test_config["vim_conn"].delete_network(net_id)
+            if result:
+                logger.info("Network id {} sucessfully deleted".format(net_id))
+            else:
+                logger.info("Failed to delete network id {}".format(net_id))
+
+    def test_020_new_network_by_ipprofile(self):
+        test_directory_content = os.listdir(test_config["test_directory"])
+
+        for dir_name in test_directory_content:
+            if dir_name == 'simple_multi_vnfc':
+                self.__class__.scenario_test_path = test_config["test_directory"] + '/'+ dir_name
+                vnfd_files = glob.glob(self.__class__.scenario_test_path+'/vnfd_*.yaml')
+                break
+
+        for vnfd in vnfd_files:
+            with open(vnfd, 'r') as stream:
+                vnf_descriptor = yaml.load(stream)
+
+            internal_connections_list = vnf_descriptor['vnf']['internal-connections']
+            for item in internal_connections_list:
+                if 'ip-profile' in item:
+                    version = item['ip-profile']['ip-version']
+                    dhcp_count = item['ip-profile']['dhcp']['count']
+                    dhcp_enabled = item['ip-profile']['dhcp']['enabled']
+
+        self.__class__.network_name = _get_random_string(20)
+        ip_profile = {'dhcp_count': dhcp_count,
+                      'dhcp_enabled': dhcp_enabled,
+                      'ip_version': version
+                     }
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+        network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                                           net_type='mgmt',
+                                                                     ip_profile=ip_profile)
+        self.__class__.network_id = network
+        logger.debug("{}".format(network))
+
+        network_list = test_config["vim_conn"].get_vcd_network_list()
+        for net in network_list:
+            if self.__class__.network_name in net.get('name'):
+                self.assertIn(self.__class__.network_name, net.get('name'))
+
+        # Deleting created network
+        result = test_config["vim_conn"].delete_network(self.__class__.network_id)
+        if result:
+            logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id))
+        else:
+            logger.info("Failed to delete network id {}".format(self.__class__.network_id))
+
+    def test_030_new_network_by_isshared(self):
+        self.__class__.network_name = _get_random_string(20)
+        shared = True
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+        network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                                         net_type='bridge',
+                                                                             shared=shared)
+        self.__class__.network_id = network
+        logger.debug("{}".format(network))
+
+        network_list = test_config["vim_conn"].get_vcd_network_list()
+        for net in network_list:
+            if self.__class__.network_name in net.get('name'):
+                self.assertIn(self.__class__.network_name, net.get('name'))
+                self.assertEqual(net.get('shared'), shared)
+
+        # Deleting created network
+        result = test_config["vim_conn"].delete_network(self.__class__.network_id)
+        if result:
+            logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id))
+        else:
+            logger.info("Failed to delete network id {}".format(self.__class__.network_id))
+
+    def test_040_new_network_by_negative(self):
+        self.__class__.network_name = _get_random_string(20)
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+        network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                                    net_type='unknowntype')
+        self.__class__.network_id = network
+        logger.debug("{}".format(network))
+        network_list = test_config["vim_conn"].get_vcd_network_list()
+        for net in network_list:
+            if self.__class__.network_name in net.get('name'):
+                self.assertIn(self.__class__.network_name, net.get('name'))
+
+        # Deleting created network
+        result = test_config["vim_conn"].delete_network(self.__class__.network_id)
+        if result:
+            logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id))
+        else:
+            logger.info("Failed to delete network id {}".format(self.__class__.network_id))
+
+class test_vimconn_get_network_list(test_base):
+    # test_index = 1
+    network_name = None
+
+    # test_text = None
+    # @classmethod
+    # def setUpClass(cls):
+    #     logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+
+    # @classmethod
+    # def tearDownClass(cls):
+    #     test_config["test_number"] += 1
+
+    def setUp(self):
+        # creating new network
+        self.__class__.network_name = _get_random_string(20)
+        self.__class__.net_type = 'bridge'
+        network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                          net_type=self.__class__.net_type)
+        self.__class__.network_id = network
+        logger.debug("{}".format(network))
+
+    def tearDown(self):
+        test_base.tearDown(self)
+        # exec_info = sys.exc_info()
+        # if exec_info == (None, None, None):
+        #     logger.info(self.__class__.test_text+" -> TEST OK")
+        # else:
+        #     logger.warning(self.__class__.test_text+" -> TEST NOK")
+        #     logger.critical("Traceback error",exc_info=True)
+
+        # Deleting created network
+        result = test_config["vim_conn"].delete_network(self.__class__.network_id)
+        if result:
+            logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id))
+        else:
+            logger.info("Failed to delete network id {}".format(self.__class__.network_id))
+
+    def test_000_get_network_list(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        network_list = test_config["vim_conn"].get_network_list()
+        for net in network_list:
+            if self.__class__.network_name in net.get('name'):
+                self.assertIn(self.__class__.network_name, net.get('name'))
+                self.assertEqual(net.get('type'), self.__class__.net_type)
+                self.assertEqual(net.get('status'), 'ACTIVE')
+                self.assertEqual(net.get('shared'), False)
+
+    def test_010_get_network_list_by_name(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        network_name = test_config['vim_conn'].get_network_name_by_id(self.__class__.network_id)
+
+        # find network from list by it's name
+        new_network_list = test_config["vim_conn"].get_network_list({'name': network_name})
+        for list_item in new_network_list:
+            if self.__class__.network_name in list_item.get('name'):
+                self.assertEqual(network_name, list_item.get('name'))
+                self.assertEqual(list_item.get('type'), self.__class__.net_type)
+                self.assertEqual(list_item.get('status'), 'ACTIVE')
+
+    def test_020_get_network_list_by_id(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        # find network from list by it's id
+        new_network_list = test_config["vim_conn"].get_network_list({'id':self.__class__.network_id})
+        for list_item in new_network_list:
+            if self.__class__.network_id in list_item.get('id'):
+                self.assertEqual(self.__class__.network_id, list_item.get('id'))
+                self.assertEqual(list_item.get('type'), self.__class__.net_type)
+                self.assertEqual(list_item.get('status'), 'ACTIVE')
+
+    def test_030_get_network_list_by_shared(self):
+        Shared = False
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        network_name = test_config['vim_conn'].get_network_name_by_id(self.__class__.network_id)
+        # find network from list by it's shared value
+        new_network_list = test_config["vim_conn"].get_network_list({'shared':Shared,
+                                                                'name':network_name})
+        for list_item in new_network_list:
+            if list_item.get('shared') == Shared:
+                self.assertEqual(list_item.get('shared'), Shared)
+                self.assertEqual(list_item.get('type'), self.__class__.net_type)
+                self.assertEqual(network_name, list_item.get('name'))
+
+    def test_040_get_network_list_by_tenant_id(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        tenant_list = test_config["vim_conn"].get_tenant_list()
+        network_name = test_config['vim_conn'].get_network_name_by_id(self.__class__.network_id)
+
+        for tenant_item in tenant_list:
+            if test_config['tenant'] == tenant_item.get('name'):
+                # find network from list by it's tenant id
+                tenant_id = tenant_item.get('id')
+                new_network_list = test_config["vim_conn"].get_network_list({'tenant_id':tenant_id,
+                                                                              'name':network_name})
+                for list_item in new_network_list:
+                    self.assertEqual(tenant_id, list_item.get('tenant_id'))
+                    self.assertEqual(network_name, list_item.get('name'))
+                    self.assertEqual(list_item.get('type'), self.__class__.net_type)
+                    self.assertEqual(list_item.get('status'), 'ACTIVE')
+
+    def test_050_get_network_list_by_status(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+        status = 'ACTIVE'
+
+        network_name = test_config['vim_conn'].get_network_name_by_id(self.__class__.network_id)
+
+        # find network from list by it's status
+        new_network_list = test_config["vim_conn"].get_network_list({'status':status,
+                                                               'name': network_name})
+        for list_item in new_network_list:
+            self.assertIn(self.__class__.network_name, list_item.get('name'))
+            self.assertEqual(list_item.get('type'), self.__class__.net_type)
+            self.assertEqual(list_item.get('status'), status)
+
+    def test_060_get_network_list_by_negative(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        network_list = test_config["vim_conn"].get_network_list({'name': 'unknown_name'})
+        self.assertEqual(network_list, [])
+
+class test_vimconn_get_network(test_base):
+    # test_index = 1
+    network_name = None
+    # test_text = None
+
+    # @classmethod
+    # def setUpClass(cls):
+    #     logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+
+    # @classmethod
+    # def tearDownClass(cls):
+    #     test_config["test_number"] += 1
+
+    def setUp(self):
+        # creating new network
+        self.__class__.network_name = _get_random_string(20)
+        self.__class__.net_type = 'bridge'
+        network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                          net_type=self.__class__.net_type)
+        self.__class__.network_id = network
+        logger.debug("{}".format(network))
+
+    def tearDown(self):
+        test_base.tearDown(self)
+        # exec_info = sys.exc_info()
+        # if exec_info == (None, None, None):
+        #     logger.info(self.__class__.test_text+" -> TEST OK")
+        # else:
+        #     logger.warning(self.__class__.test_text+" -> TEST NOK")
+        #     logger.critical("Traceback error",exc_info=True)
+
+        # Deleting created network
+        result = test_config["vim_conn"].delete_network(self.__class__.network_id)
+        if result:
+            logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id))
+        else:
+            logger.info("Failed to delete network id {}".format(self.__class__.network_id))
+
+    def test_000_get_network(self):
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        network_info = test_config["vim_conn"].get_network(self.__class__.network_id)
+        self.assertEqual(network_info.get('status'), 'ACTIVE')
+        self.assertIn(self.__class__.network_name, network_info.get('name'))
+        self.assertEqual(network_info.get('type'), self.__class__.net_type)
+        self.assertEqual(network_info.get('id'), self.__class__.network_id)
+
+    def test_010_get_network_negative(self):
+        Non_exist_id = str(uuid.uuid4())
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        network_info = test_config["vim_conn"].get_network(Non_exist_id)
+        self.assertEqual(network_info, {})
+
+class test_vimconn_delete_network(test_base):
+    # test_index = 1
+    network_name = None
+    # test_text = None
+
+    # @classmethod
+    # def setUpClass(cls):
+    #     logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+
+    # @classmethod
+    # def tearDownClass(cls):
+    #     test_config["test_number"] += 1
+
+    # def tearDown(self):
+    #     exec_info = sys.exc_info()
+    #     if exec_info == (None, None, None):
+    #         logger.info(self.__class__.test_text+" -> TEST OK")
+    #     else:
+    #         logger.warning(self.__class__.test_text+" -> TEST NOK")
+    #         logger.critical("Traceback error",exc_info=True)
+
+    def test_000_delete_network(self):
+        # Creating network
+        self.__class__.network_name = _get_random_string(20)
+        self.__class__.net_type = 'bridge'
+        network = test_config["vim_conn"].new_network(net_name=self.__class__.network_name,
+                                                          net_type=self.__class__.net_type)
+        self.__class__.network_id = network
+        logger.debug("{}".format(network))
+
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        result = test_config["vim_conn"].delete_network(self.__class__.network_id)
+        if result:
+            logger.info("Network id {} sucessfully deleted".format(self.__class__.network_id))
+        else:
+            logger.info("Failed to delete network id {}".format(self.__class__.network_id))
+        time.sleep(5)
+        # after deleting network we check in network list
+        network_list = test_config["vim_conn"].get_network_list({ 'id':self.__class__.network_id })
+        self.assertEqual(network_list, [])
+
+    def test_010_delete_network_negative(self):
+        Non_exist_id = str(uuid.uuid4())
+
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        with self.assertRaises(Exception) as context:
+            test_config["vim_conn"].delete_network(Non_exist_id)
+
+        self.assertEqual((context.exception).http_code, 400)
+
+class test_vimconn_get_flavor(test_base):
+    # test_index = 1
+    # test_text = None
+
+    # @classmethod
+    # def setUpClass(cls):
+    #     logger.info("{}. {}".format(test_config["test_number"], cls.__name__))
+
+    # @classmethod
+    # def tearDownClass(cls):
+    #     test_config["test_number"] += 1
+
+    # def tearDown(self):
+    #     exec_info = sys.exc_info()
+    #     if exec_info == (None, None, None):
+    #         logger.info(self.__class__.test_text+" -> TEST OK")
+    #     else:
+    #         logger.warning(self.__class__.test_text+" -> TEST NOK")
+    #         logger.critical("Traceback error",exc_info=True)
+
+    def test_000_get_flavor(self):
+        test_directory_content = os.listdir(test_config["test_directory"])
+
+        for dir_name in test_directory_content:
+            if dir_name == 'simple_linux':
+                self.__class__.scenario_test_path = test_config["test_directory"] + '/'+ dir_name
+                vnfd_files = glob.glob(self.__class__.scenario_test_path+'/vnfd_*.yaml')
+                break
+
+        for vnfd in vnfd_files:
+            with open(vnfd, 'r') as stream:
+                vnf_descriptor = yaml.load(stream)
+
+            vnfc_list = vnf_descriptor['vnf']['VNFC']
+            for item in vnfc_list:
+                if 'ram' in item and 'vcpus' in item and 'disk' in item:
+                    ram = item['ram']
+                    vcpus = item['vcpus']
+                    disk = item['disk']
+
+        flavor_data = {'ram': ram,
+                      'vcpus': vcpus,
+                      'disk': disk
+                      }
+
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+        # create new flavor
+        flavor_id = test_config["vim_conn"].new_flavor(flavor_data)
+        # get flavor by id
+        result = test_config["vim_conn"].get_flavor(flavor_id)
+        self.assertEqual(ram, result['ram'])
+        self.assertEqual(vcpus, result['vcpus'])
+        self.assertEqual(disk, result['disk'])
+
+        # delete flavor
+        result = test_config["vim_conn"].delete_flavor(flavor_id)
+        if result:
+            logger.info("Flavor id {} sucessfully deleted".format(result))
+        else:
+            logger.info("Failed to delete flavor id {}".format(result))
+
+    def test_010_get_flavor_negative(self):
+        Non_exist_flavor_id = str(uuid.uuid4())
+
+        self.__class__.test_text = "{}.{}. TEST {}".format(test_config["test_number"],
+                                                            self.__class__.test_index,
+                                                inspect.currentframe().f_code.co_name)
+        self.__class__.test_index += 1
+
+        with self.assertRaises(Exception) as context:
+            test_config["vim_conn"].get_flavor(Non_exist_flavor_id)
+
+        self.assertEqual((context.exception).http_code, 404)
+
+
 '''
 IMPORTANT NOTE
 The following unittest class does not have the 'test_' on purpose. This test is the one used for the
 scenario based tests.
 '''
-class descriptor_based_scenario_test(unittest.TestCase):
+class descriptor_based_scenario_test(test_base):
     test_index = 0
-    test_text = None
     scenario_test_path = None
     scenario_uuid = None
     instance_scenario_uuid = None
@@ -386,19 +849,6 @@ class descriptor_based_scenario_test(unittest.TestCase):
     def tearDownClass(cls):
          test_config["test_number"] += 1
 
-    def tearDown(self):
-        exec_info = sys.exc_info()
-        if exec_info == (None, None, None):
-            logger.info(self.__class__.test_text + " -> TEST OK")
-        else:
-            logger.warning(self.__class__.test_text + " -> TEST NOK")
-            error_trace = traceback.format_exception(exec_info[0], exec_info[1], exec_info[2])
-            msg = ""
-            for line in error_trace:
-                msg = msg + line
-            logger.critical("{}".format(msg))
-
-
     def test_000_load_scenario(self):
         self.__class__.test_text = "{}.{}. TEST {} {}".format(test_config["test_number"], self.__class__.test_index,
                                                            inspect.currentframe().f_code.co_name,
@@ -515,8 +965,23 @@ def _get_random_string(maxLength):
 def test_vimconnector(args):
     global test_config
     sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/osm_ro")
+    test_config['vimtype'] = args.vimtype
     if args.vimtype == "vmware":
         import vimconn_vmware as vim
+
+        test_config["test_directory"] = os.path.dirname(__file__) + "/RO_tests"
+
+        tenant_name = args.tenant_name
+        test_config['tenant'] = tenant_name
+        config_params = json.loads(args.config_param)
+        org_name = config_params.get('orgname')
+        org_user = config_params.get('user')
+        org_passwd = config_params.get('passwd')
+        vim_url = args.endpoint_url
+
+        # vmware connector obj
+        test_config['vim_conn'] = vim.vimconnector(name=org_name, tenant_name=tenant_name, user=org_user,passwd=org_passwd, url=vim_url, config=config_params)
+
     elif args.vimtype == "aws":
         import vimconn_aws as vim
     elif args.vimtype == "openstack":
@@ -660,9 +1125,9 @@ def test_deploy(args):
     test_directory_content = os.listdir(test_config["test_directory"])
     # If only want to obtain a tests list print it and exit
     if args.list_tests:
-        msg = "he 'deploy' set tests are:\n\t" + ', '.join(sorted(test_directory_content))
+        msg = "the 'deploy' set tests are:\n\t" + ', '.join(sorted(test_directory_content))
         print(msg)
-        logger.info(msg)
+        logger.info(msg)
         sys.exit(0)
 
     descriptor_based_tests = []
@@ -764,9 +1229,9 @@ if __name__=="__main__":
     mandatory_arguments = vimconn_parser.add_argument_group('mandatory arguments')
     mandatory_arguments.add_argument('--vimtype', choices=['vmware', 'aws', 'openstack', 'openvim'], required=True,
                                      help='Set the vimconnector type to test')
-    # TODO add mandatory arguments for vimconn test
-    # mandatory_arguments.add_argument('-c', '--config', dest='config_param', required=True, help='<HELP>')
-
+    mandatory_arguments.add_argument('-c', '--config', dest='config_param', required=True,
+                                    help='Set the vimconnector specific config parameters in dictionary format')
+    mandatory_arguments.add_argument('-u', '--url', dest='endpoint_url',required=True, help="Set the vim connector url or Host IP")
     # Optional arguments
     # TODO add optional arguments for vimconn tests
     # vimconn_parser.add_argument("-i", '--image-name', dest='image_name', help='<HELP>'))