Merge branch 'v1.1' 74/1074/1
authortierno <alfonso.tiernosepulveda@telefonica.com>
Tue, 7 Feb 2017 09:30:32 +0000 (10:30 +0100)
committertierno <alfonso.tiernosepulveda@telefonica.com>
Tue, 7 Feb 2017 09:30:32 +0000 (10:30 +0100)
13 files changed:
console_proxy_thread.py
httpserver.py
nfvo.py
nfvo_db.py
openmano
openmano_schemas.py
openmanod.cfg
openmanod.py
vimconn.py
vimconn_openstack.py
vimconn_openvim.py
vimconn_vmware.py
vnfs/examples/linux-cloud-init.yaml

index 67da12c..460a4aa 100644 (file)
@@ -39,6 +39,7 @@ __date__ ="$19-nov-2015 09:07:15$"
 import socket
 import select
 import threading
+import logging
 
 
 class ConsoleProxyException(Exception):
@@ -50,7 +51,7 @@ class ConsoleProxyThread(threading.Thread):
     buffer_size = 4096
     check_finish = 1 #frequency to check if requested to end in seconds
 
-    def __init__(self, host, port, console_host, console_port):
+    def __init__(self, host, port, console_host, console_port, log_level=None):
         try:
             threading.Thread.__init__(self)
             self.console_host = console_host
@@ -69,6 +70,10 @@ class ConsoleProxyThread(threading.Thread):
             self.input_list = [self.server]
             self.channel = {}
             self.terminate = False #put at True from outside to force termination
+            self.logger = logging.getLogger('openmano.console')
+            if log_level:
+                self.logger.setLevel( getattr(logging, log_level) )
+
         except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
             if e is socket.error and e.errno==98:
                 raise ConsoleProxyExceptionPortUsed("socket.error " + str(e))
@@ -79,12 +84,12 @@ class ConsoleProxyThread(threading.Thread):
             try:
                 inputready, _, _ = select.select(self.input_list, [], [], self.check_finish)
             except select.error as e:
-                print self.name, ": Exception on select %s: %s" % (type(e).__name__, str(e) )
+                self.logger.error("Exception on select %s: %s", type(e).__name__, str(e) )
                 self.on_terminate()
 
             if self.terminate:
                 self.on_terminate()
-                print self.name, ": Terminate because commanded"
+                self.logger.debug("Terminate because commanded")
                 break
             
             for sock in inputready:
@@ -106,7 +111,7 @@ class ConsoleProxyThread(threading.Thread):
         try:
             clientsock, clientaddr = self.server.accept()
         except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
-            print self.name, ": Exception on_accept %s: %s" % (type(e).__name__, str(e) )
+            self.logger.error("Exception on_accept %s: %s", type(e).__name__, str(e) )
             return False
         #print self.name, ": Accept new client ", clientaddr
 
@@ -116,7 +121,7 @@ class ConsoleProxyThread(threading.Thread):
             forward.connect((self.console_host, self.console_port))
             name = "%s:%d => (%s:%d => %s:%d) => %s:%d" %\
                 (clientsock.getpeername() + clientsock.getsockname()  + forward.getsockname() + forward.getpeername() )
-            print self.name, ": new connection " + name
+            self.logger.warn("new connection " + name)
                 
             self.input_list.append(clientsock)
             self.input_list.append(forward)
@@ -128,8 +133,8 @@ class ConsoleProxyThread(threading.Thread):
             self.channel[forward] = info
             return True
         except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
-            print self.name, ": Exception on_connect to server %s:%d; %s: %s" % (self.console_host, self.console_port, type(e).__name__, str(e) )
-            print self.name, ": Close client side ", clientaddr
+            self.logger.error("Exception on_connect to server %s:%d; %s: %s  Close client side %s",
+                self.console_host, self.console_port, type(e).__name__, str(e), str(clientaddr) )
             clientsock.close()
             return False
 
@@ -139,18 +144,18 @@ class ConsoleProxyThread(threading.Thread):
         info = self.channel[sock]
         #debug info
         sockname = "client" if sock is info["clientsock"] else "server"
-        print self.name, ": del connection %s %s at %s side" % (info["name"], cause, sockname)
+        self.logger.warn("del connection %s %s at %s side", info["name"], str(cause), str(sockname) )
         #close sockets
         try:
             # close the connection with client
             info["clientsock"].close()  # equivalent to do self.s.close()
         except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
-            print self.name, ": Exception on_close client socket %s: %s" % (type(e).__name__, str(e) )
+            self.logger.error("Exception on_close client socket %s: %s", type(e).__name__, str(e) )
         try:
             # close the connection with remote server
             info["serversock"].close()
         except (socket.error, socket.herror, socket.gaierror, socket.timeout) as e:
-            print self.name, ": Exception on_close server socket %s: %s" % (type(e).__name__, str(e) )
+            self.logger.error("Exception on_close server socket %s: %s", type(e).__name__, str(e) )
         
         #remove objects from input_list
         self.input_list.remove(info["clientsock"])
index 9dd537f..64eddfa 100644 (file)
@@ -996,14 +996,14 @@ def http_post_scenarios(tenant_id):
 def http_post_scenario_action(tenant_id, scenario_id):
     '''take an action over a scenario'''
     logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
-    #check valid tenant_id
+    # parse input data
+    http_content, _ = format_in(scenario_action_schema)
+    r = utils.remove_extra_items(http_content, scenario_action_schema)
+    if r:
+        logger.debug("Remove received extra items %s", str(r))
     try:
-        nfvo.check_tenant(mydb, tenant_id) 
-        #parse input data
-        http_content,_ = format_in( scenario_action_schema )
-        r = utils.remove_extra_items(http_content, scenario_action_schema)
-        if r:
-            logger.debug("Remove received extra items %s", str(r))
+        # check valid tenant_id
+        nfvo.check_tenant(mydb, tenant_id)
         if "start" in http_content:
             data = nfvo.start_scenario(mydb, tenant_id, scenario_id, http_content['start']['instance_name'], \
                         http_content['start'].get('description',http_content['start']['instance_name']),
@@ -1086,10 +1086,11 @@ def http_get_scenario_id(tenant_id, scenario_id):
 @bottle.route(url_base + '/<tenant_id>/scenarios/<scenario_id>', method='DELETE')
 def http_delete_scenario_id(tenant_id, scenario_id):
     '''delete a scenario from database, can use both uuid or name'''
+    logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
     try:
         #check valid tenant_id
         if tenant_id != "any":
-            nfvo.check_tenant(mydb, tenant_id) 
+            nfvo.check_tenant(mydb, tenant_id)
         #obtain data
         data = mydb.delete_scenario(scenario_id, tenant_id)
         #print json.dumps(data, indent=4)
@@ -1126,15 +1127,15 @@ def http_put_scenario_id(tenant_id, scenario_id):
 def http_post_instances(tenant_id):
     '''create an instance-scenario'''
     logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+    # parse input data
+    http_content, used_schema = format_in(instance_scenario_create_schema_v01)
+    r = utils.remove_extra_items(http_content, used_schema)
+    if r is not None:
+        logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
     try:
         #check valid tenant_id
         if tenant_id != "any":
             nfvo.check_tenant(mydb, tenant_id) 
-        #parse input data
-        http_content,used_schema = format_in( instance_scenario_create_schema_v01)
-        r = utils.remove_extra_items(http_content, used_schema)
-        if r is not None:
-            logger.warning("http_post_instances: Warning: remove extra items %s", str(r))
         data = nfvo.create_instance(mydb, tenant_id, http_content["instance"])
         return format_out(data)
     except (nfvo.NfvoException, db_base_Exception) as e:
@@ -1225,16 +1226,16 @@ def http_delete_instance_id(tenant_id, instance_id):
 def http_post_instance_scenario_action(tenant_id, instance_id):
     '''take an action over a scenario instance'''
     logger.debug('FROM %s %s %s', bottle.request.remote_addr, bottle.request.method, bottle.request.url)
+    # parse input data
+    http_content, _ = format_in(instance_scenario_action_schema)
+    r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
+    if r:
+        logger.debug("Remove received extra items %s", str(r))
     try:
         #check valid tenant_id
         if tenant_id != "any":
             nfvo.check_tenant(mydb, tenant_id) 
 
-        #parse input data
-        http_content,_ = format_in( instance_scenario_action_schema )
-        r = utils.remove_extra_items(http_content, instance_scenario_action_schema)
-        if r:
-            logger.debug("Remove received extra items %s", str(r))
         #print "http_post_instance_scenario_action input: ", http_content
         #obtain data
         instance = mydb.get_instance_scenario(instance_id, tenant_id)
diff --git a/nfvo.py b/nfvo.py
index 7537c75..c8ef020 100644 (file)
--- a/nfvo.py
+++ b/nfvo.py
@@ -194,28 +194,30 @@ def rollback(mydb,  vims, rollback_list):
     else:
         return False," Rollback fails to delete: " + str(undeleted_items)
   
-def check_vnf_descriptor(vnf_descriptor):
+def check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1):
     global global_config
     #create a dictionary with vnfc-name: vnfc:interface-list  key:values pairs 
     vnfc_interfaces={}
     for vnfc in vnf_descriptor["vnf"]["VNFC"]:
-        name_list = []
+        name_dict = {}
         #dataplane interfaces
         for numa in vnfc.get("numas",() ):
             for interface in numa.get("interfaces",()):
-                if interface["name"] in name_list:
-                    raise NfvoException("Error at vnf:VNFC[name:'{}']:numas:interfaces:name, interface name '{}' already used in this VNFC"\
-                                        .format(vnfc["name"], interface["name"]), 
-                                        HTTP_Bad_Request) 
-                name_list.append( interface["name"] )
+                if interface["name"] in name_dict:
+                    raise NfvoException(
+                        "Error at vnf:VNFC[name:'{}']:numas:interfaces:name, interface name '{}' already used in this VNFC".format(
+                            vnfc["name"], interface["name"]),
+                        HTTP_Bad_Request)
+                name_dict[ interface["name"] ] = "underlay"
         #bridge interfaces
         for interface in vnfc.get("bridge-ifaces",() ):
-            if interface["name"] in name_list:
-                raise NfvoException("Error at vnf:VNFC[name:'{}']:bridge-ifaces:name, interface name '{}' already used in this VNFC"\
-                                    .format(vnfc["name"], interface["name"]),
-                                    HTTP_Bad_Request)
-            name_list.append( interface["name"] ) 
-        vnfc_interfaces[ vnfc["name"] ] = name_list
+            if interface["name"] in name_dict:
+                raise NfvoException(
+                    "Error at vnf:VNFC[name:'{}']:bridge-ifaces:name, interface name '{}' already used in this VNFC".format(
+                        vnfc["name"], interface["name"]),
+                    HTTP_Bad_Request)
+            name_dict[ interface["name"] ] = "overlay"
+        vnfc_interfaces[ vnfc["name"] ] = name_dict
         # check bood-data info
         if "boot-data" in vnfc:
             # check that user-data is incompatible with users and config-files
@@ -228,43 +230,83 @@ def check_vnf_descriptor(vnf_descriptor):
     name_list=[]
     for external_connection in vnf_descriptor["vnf"].get("external-connections",() ):
         if external_connection["name"] in name_list:
-            raise NfvoException("Error at vnf:external-connections:name, value '{}' already used as an external-connection"\
-                                .format(external_connection["name"]),
-                                HTTP_Bad_Request)
+            raise NfvoException(
+                "Error at vnf:external-connections:name, value '{}' already used as an external-connection".format(
+                    external_connection["name"]),
+                HTTP_Bad_Request)
         name_list.append(external_connection["name"])
         if external_connection["VNFC"] not in vnfc_interfaces:
-            raise NfvoException("Error at vnf:external-connections[name:'{}']:VNFC, value '{}' does not match any VNFC"\
-                                .format(external_connection["name"], external_connection["VNFC"]),
-                                HTTP_Bad_Request)
+            raise NfvoException(
+                "Error at vnf:external-connections[name:'{}']:VNFC, value '{}' does not match any VNFC".format(
+                    external_connection["name"], external_connection["VNFC"]),
+                HTTP_Bad_Request)
             
         if external_connection["local_iface_name"] not in vnfc_interfaces[ external_connection["VNFC"] ]:
-            raise NfvoException("Error at vnf:external-connections[name:'{}']:local_iface_name, value '{}' does not match any interface of this VNFC"\
-                                .format(external_connection["name"], external_connection["local_iface_name"]),
-                                HTTP_Bad_Request )
+            raise NfvoException(
+                "Error at vnf:external-connections[name:'{}']:local_iface_name, value '{}' does not match any interface of this VNFC".format(
+                    external_connection["name"],
+                    external_connection["local_iface_name"]),
+                HTTP_Bad_Request )
     
     #check if the info in internal_connections matches with the one in the vnfcs
     name_list=[]
     for internal_connection in vnf_descriptor["vnf"].get("internal-connections",() ):
         if internal_connection["name"] in name_list:
-            raise NfvoException("Error at vnf:internal-connections:name, value '%s' already used as an internal-connection"\
-                                .format(internal_connection["name"]),
-                                HTTP_Bad_Request)
+            raise NfvoException(
+                "Error at vnf:internal-connections:name, value '%s' already used as an internal-connection".format(
+                    internal_connection["name"]),
+                HTTP_Bad_Request)
         name_list.append(internal_connection["name"])
         #We should check that internal-connections of type "ptp" have only 2 elements
-        if len(internal_connection["elements"])>2 and internal_connection["type"] == "ptp":
-            raise NfvoException("Error at vnf:internal-connections[name:'{}']:elements, size must be 2 for a type:'ptp'"\
-                                .format(internal_connection["name"]),
-                                HTTP_Bad_Request)
+
+        if len(internal_connection["elements"])>2 and (internal_connection.get("type") == "ptp" or internal_connection.get("type") == "e-line"):
+            raise NfvoException(
+                "Error at 'vnf:internal-connections[name:'{}']:elements', size must be 2 for a '{}' type. Consider change it to '{}' type".format(
+                    internal_connection["name"],
+                    'ptp' if vnf_descriptor_version==1 else 'e-line',
+                    'data' if vnf_descriptor_version==1 else "e-lan"),
+                HTTP_Bad_Request)
         for port in internal_connection["elements"]:
-            if port["VNFC"] not in vnfc_interfaces:
-                raise NfvoException("Error at vnf:internal-connections[name:'{}']:elements[]:VNFC, value '{}' does not match any VNFC"\
-                                    .format(internal_connection["name"], port["VNFC"]),
-                                    HTTP_Bad_Request)
-            if port["local_iface_name"] not in vnfc_interfaces[ port["VNFC"] ]:
-                raise NfvoException("Error at vnf:internal-connections[name:'{}']:elements[]:local_iface_name, value '{}' does not match any interface of this VNFC"\
-                                    .format(internal_connection["name"], port["local_iface_name"]),
-                                    HTTP_Bad_Request)
-                return -HTTP_Bad_Request, 
+            vnf = port["VNFC"]
+            iface = port["local_iface_name"]
+            if vnf not in vnfc_interfaces:
+                raise NfvoException(
+                    "Error at vnf:internal-connections[name:'{}']:elements[]:VNFC, value '{}' does not match any VNFC".format(
+                        internal_connection["name"], vnf),
+                    HTTP_Bad_Request)
+            if iface not in vnfc_interfaces[ vnf ]:
+                raise NfvoException(
+                    "Error at vnf:internal-connections[name:'{}']:elements[]:local_iface_name, value '{}' does not match any interface of this VNFC".format(
+                        internal_connection["name"], iface),
+                    HTTP_Bad_Request)
+                return -HTTP_Bad_Request,
+            if vnf_descriptor_version==1 and "type" not in internal_connection:
+                if vnfc_interfaces[vnf][iface] == "overlay":
+                    internal_connection["type"] = "bridge"
+                else:
+                    internal_connection["type"] = "data"
+            if vnf_descriptor_version==2 and "implementation" not in internal_connection:
+                if vnfc_interfaces[vnf][iface] == "overlay":
+                    internal_connection["implementation"] = "overlay"
+                else:
+                    internal_connection["implementation"] = "underlay"
+            if (internal_connection.get("type") == "data" or internal_connection.get("type") == "ptp" or \
+                internal_connection.get("implementation") == "underlay") and vnfc_interfaces[vnf][iface] == "overlay":
+                raise NfvoException(
+                    "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format(
+                        internal_connection["name"],
+                        iface, 'bridge' if vnf_descriptor_version==1 else 'overlay',
+                        'data' if vnf_descriptor_version==1 else 'underlay'),
+                    HTTP_Bad_Request)
+            if (internal_connection.get("type") == "bridge" or internal_connection.get("implementation") == "overlay") and \
+                vnfc_interfaces[vnf][iface] == "underlay":
+                raise NfvoException(
+                    "Error at vnf:internal-connections[name:'{}']:elements[]:{}, interface of type {} connected to an {} network".format(
+                        internal_connection["name"], iface,
+                        'data' if vnf_descriptor_version==1 else 'underlay',
+                        'bridge' if vnf_descriptor_version==1 else 'overlay'),
+                    HTTP_Bad_Request)
+
 
 def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vim=False, return_on_error = None):
     #look if image exist
@@ -322,7 +364,8 @@ def create_or_use_image(mydb, vims, image_dict, rollback_list, only_create_at_vi
                     rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"image","uuid":image_vim_id})
                     image_created="true"
                 else:
-                    raise vimconn.vimconnException("Cannot create image without location")
+                    #If we reach this point, then the image has image name, and optionally checksum, and could not be found
+                    raise vimconn.vimconnException(str(e))
             except vimconn.vimconnException as e:
                 if return_on_error:
                     logger.error("Error creating image at VIM '%s': %s", vim["name"], str(e))
@@ -465,9 +508,16 @@ def create_or_use_flavor(mydb, vims, flavor_dict, rollback_list, only_create_at_
         #create flavor at vim
         logger.debug("nfvo.create_or_use_flavor() adding flavor to VIM %s", vim["name"])
         try:
-            flavor_vim_id = vim.new_flavor(flavor_dict)
-            rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"flavor","uuid":flavor_vim_id})
-            flavor_created="true"
+            flavor_vim_id = None
+            flavor_vim_id=vim.get_flavor_id_from_data(flavor_dict)
+            flavor_create="false"
+        except vimconn.vimconnException as e:
+            pass
+        try:
+            if not flavor_vim_id:
+                flavor_vim_id = vim.new_flavor(flavor_dict)
+                rollback_list.append({"where":"vim", "vim_id": vim_id, "what":"flavor","uuid":flavor_vim_id})
+                flavor_created="true"
         except vimconn.vimconnException as e:
             if return_on_error:
                 logger.error("Error creating flavor at VIM %s: %s.", vim["name"], str(e))
@@ -496,8 +546,9 @@ def new_vnf(mydb, tenant_id, vnf_descriptor):
     global global_config
     
     # Step 1. Check the VNF descriptor
-    check_vnf_descriptor(vnf_descriptor)
+    check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=1)
     # Step 2. Check tenant exist
+    vims = {}
     if tenant_id != "any":
         check_tenant(mydb, tenant_id) 
         if "tenant_id" in vnf_descriptor["vnf"]:
@@ -507,9 +558,8 @@ def new_vnf(mydb, tenant_id, vnf_descriptor):
         else:
             vnf_descriptor['vnf']['tenant_id'] = tenant_id
         # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
-        vims = get_vim(mydb, tenant_id)
-    else:
-        vims={}
+        if global_config["auto_push_VNF_to_VIMs"]:
+            vims = get_vim(mydb, tenant_id)
 
     # Step 4. Review the descriptor and add missing  fields
     #print vnf_descriptor
@@ -519,17 +569,7 @@ def new_vnf(mydb, tenant_id, vnf_descriptor):
     if "physical" in vnf_descriptor['vnf']:
         del vnf_descriptor['vnf']['physical']
     #print vnf_descriptor
-    # Step 5. Check internal connections
-    # TODO: to be moved to step 1????
-    internal_connections=vnf_descriptor['vnf'].get('internal_connections',[])
-    for ic in internal_connections:
-        if len(ic['elements'])>2 and ic['type']=='ptp':
-            raise NfvoException("Mismatch 'type':'ptp' with {} elements at 'vnf':'internal-conections'['name':'{}']. Change 'type' to 'data'".format(len(ic), ic['name']), 
-                                HTTP_Bad_Request)
-        elif len(ic['elements'])==2 and ic['type']=='data':
-            raise NfvoException("Mismatch 'type':'data' with 2 elements at 'vnf':'internal-conections'['name':'{}']. Change 'type' to 'ptp'".format(ic['name']),
-                                 HTTP_Bad_Request)
-        
+
     # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM 
     logger.debug('BEGIN creation of VNF "%s"' % vnf_name)
     logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC'])))
@@ -640,8 +680,9 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor):
     global global_config
     
     # Step 1. Check the VNF descriptor
-    check_vnf_descriptor(vnf_descriptor)
+    check_vnf_descriptor(vnf_descriptor, vnf_descriptor_version=2)
     # Step 2. Check tenant exist
+    vims = {}
     if tenant_id != "any":
         check_tenant(mydb, tenant_id) 
         if "tenant_id" in vnf_descriptor["vnf"]:
@@ -651,9 +692,8 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor):
         else:
             vnf_descriptor['vnf']['tenant_id'] = tenant_id
         # Step 3. Get the URL of the VIM from the nfvo_tenant and the datacenter
-        vims = get_vim(mydb, tenant_id)
-    else:
-        vims={}
+        if global_config["auto_push_VNF_to_VIMs"]:
+            vims = get_vim(mydb, tenant_id)
 
     # Step 4. Review the descriptor and add missing  fields
     #print vnf_descriptor
@@ -663,14 +703,7 @@ def new_vnf_v02(mydb, tenant_id, vnf_descriptor):
     if "physical" in vnf_descriptor['vnf']:
         del vnf_descriptor['vnf']['physical']
     #print vnf_descriptor
-    # Step 5. Check internal connections
-    # TODO: to be moved to step 1????
-    internal_connections=vnf_descriptor['vnf'].get('internal_connections',[])
-    for ic in internal_connections:
-        if len(ic['elements'])>2 and ic['type']=='e-line':
-            raise NfvoException("Mismatch 'type':'e-line' with {} elements at 'vnf':'internal-conections'['name':'{}']. Change 'type' to 'e-lan'".format(len(ic), ic['name']), 
-                                HTTP_Bad_Request)
-        
+
     # Step 6. For each VNFC in the descriptor, flavors and images are created in the VIM 
     logger.debug('BEGIN creation of VNF "%s"' % vnf_name)
     logger.debug("VNF %s: consisting of %d VNFC(s)" % (vnf_name,len(vnf_descriptor['vnf']['VNFC'])))
@@ -2759,6 +2792,8 @@ def vim_action_get(mydb, tenant_id, datacenter, item, name):
             content = myvim.get_network_list(filter_dict=filter_dict)
         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)
         logger.debug("vim_action response %s", content) #update nets Change from VIM format to NFVO format
@@ -2796,6 +2831,8 @@ def vim_action_delete(mydb, tenant_id, datacenter, item, name):
             content = myvim.delete_network(item_id)
         elif item=="tenants":
             content = myvim.delete_tenant(item_id)
+        elif item == "images":
+            content = myvim.delete_image(item_id)
         else:
             raise NfvoException(item + "?", HTTP_Method_Not_Allowed)    
     except vimconn.vimconnException as e:
index 11ae450..50f258c 100644 (file)
@@ -75,24 +75,13 @@ class nfvo_db(db_base.db_base):
                         #print "Internal vm id in NFVO DB: %s" % vm_id
                         vmDict[vm['name']] = vm_id
                 
-                    #Collect the data interfaces of each VM/VNFC under the 'numas' field
-                    dataifacesDict = {}
-                    for vm in vnf_descriptor['vnf']['VNFC']:
-                        dataifacesDict[vm['name']] = {}
-                        for numa in vm.get('numas', []):
-                            for dataiface in numa.get('interfaces',[]):
-                                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']]['bw'] = dataiface['bandwidth']
-                                dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface['dedicated']=="yes" else ("VF"  if dataiface['dedicated']=="no" else "VFnotShared")
-    
                     #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field
                     bridgeInterfacesDict = {}
                     for vm in vnf_descriptor['vnf']['VNFC']:
                         if 'bridge-ifaces' in  vm:
                             bridgeInterfacesDict[vm['name']] = {}
                             for bridgeiface in vm['bridge-ifaces']:
+                                created_time += 0.00001
                                 if 'port-security' in bridgeiface:
                                     bridgeiface['port_security'] = bridgeiface.pop('port-security')
                                 if 'floating-ip' in bridgeiface:
@@ -107,7 +96,24 @@ class nfvo_db(db_base.db_base):
                                     int(bridgeiface.get('port_security', True))
                                 bridgeInterfacesDict[vm['name']][bridgeiface['name']]['floating_ip'] = \
                                     int(bridgeiface.get('floating_ip', False))
-                    
+                                bridgeInterfacesDict[vm['name']][bridgeiface['name']]['created_time'] = created_time
+
+                    # Collect the data interfaces of each VM/VNFC under the 'numas' field
+                    dataifacesDict = {}
+                    for vm in vnf_descriptor['vnf']['VNFC']:
+                        dataifacesDict[vm['name']] = {}
+                        for numa in vm.get('numas', []):
+                            for dataiface in numa.get('interfaces', []):
+                                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']]['bw'] = dataiface['bandwidth']
+                                dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface[
+                                                                                                     'dedicated'] == "yes" else (
+                                "VF" if dataiface['dedicated'] == "no" else "VFnotShared")
+                                dataifacesDict[vm['name']][dataiface['name']]['created_time'] = created_time
+
                     #For each internal connection, we add it to the interfaceDict and we  create the appropriate net in the NFVO database.
                     #print "Adding new nets (VNF internal nets) to the NFVO database (if any)"
                     internalconnList = []
@@ -133,26 +139,27 @@ class nfvo_db(db_base.db_base):
                                 ifaceItem["net_id"] = net_id
                                 ifaceItem["type"] = net['type']
                                 if ifaceItem ["type"] == "data":
-                                    ifaceItem["vpci"] =  dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci'] 
-                                    ifaceItem["bw"] =    dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw']
-                                    ifaceItem["model"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model']
+                                    dataiface = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+                                    ifaceItem["vpci"] =  dataiface['vpci']
+                                    ifaceItem["bw"] =    dataiface['bw']
+                                    ifaceItem["model"] = dataiface['model']
+                                    created_time_iface = dataiface['created_time']
                                 else:
-                                    ifaceItem["vpci"] =  bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci']
-                                    ifaceItem["mac"] =  bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['mac_address']
-                                    ifaceItem["bw"] =    bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw']
-                                    ifaceItem["model"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model']
-                                    ifaceItem["port_security"] = \
-                                    bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['port_security']
-                                    ifaceItem["floating_ip"] = \
-                                    bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['floating_ip']
+                                    bridgeiface =  bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+                                    ifaceItem["vpci"]          = bridgeiface['vpci']
+                                    ifaceItem["mac"]           = bridgeiface['mac']
+                                    ifaceItem["bw"]            = bridgeiface['bw']
+                                    ifaceItem["model"]         = bridgeiface['model']
+                                    ifaceItem["port_security"] = bridgeiface['port_security']
+                                    ifaceItem["floating_ip"]   = bridgeiface['floating_ip']
+                                    created_time_iface = bridgeiface['created_time']
                                 internalconnList.append(ifaceItem)
                             #print "Internal net id in NFVO DB: %s" % net_id
                     
                     #print "Adding internal interfaces to the NFVO database (if any)"
                     for iface in internalconnList:
-                        print "Iface name: %s" % iface['internal_name']
-                        created_time += 0.00001
-                        iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
+                        #print "Iface name: %s" % iface['internal_name']
+                        iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface)
                         #print "Iface id in NFVO DB: %s" % iface_id
                     
                     #print "Adding external interfaces to the NFVO database"
@@ -165,21 +172,22 @@ class nfvo_db(db_base.db_base):
                         myIfaceDict["external_name"] = iface['name']
                         myIfaceDict["type"] = iface['type']
                         if iface["type"] == "data":
-                            myIfaceDict["vpci"]  = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
-                            myIfaceDict["bw"]    = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
-                            myIfaceDict["model"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model']
+                            dataiface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]
+                            myIfaceDict["vpci"]         = dataiface['vpci']
+                            myIfaceDict["bw"]           = dataiface['bw']
+                            myIfaceDict["model"]        = dataiface['model']
+                            created_time_iface = dataiface['created_time']
                         else:
-                            myIfaceDict["vpci"]  = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
-                            myIfaceDict["bw"]    = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
-                            myIfaceDict["model"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model']
-                            myIfaceDict["mac"] = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['mac']
-                            myIfaceDict["port_security"] = \
-                                bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['port_security']
-                            myIfaceDict["floating_ip"] = \
-                                bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['floating_ip']
-                        print "Iface name: %s" % iface['name']
-                        created_time += 0.00001
-                        iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
+                            bridgeiface = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]
+                            myIfaceDict["vpci"]         = bridgeiface['vpci']
+                            myIfaceDict["bw"]           = bridgeiface['bw']
+                            myIfaceDict["model"]        = bridgeiface['model']
+                            myIfaceDict["mac"]          = bridgeiface['mac']
+                            myIfaceDict["port_security"]= bridgeiface['port_security']
+                            myIfaceDict["floating_ip"]  = bridgeiface['floating_ip']
+                            created_time_iface = bridgeiface['created_time']
+                        #print "Iface name: %s" % iface['name']
+                        iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time = created_time_iface)
                         #print "Iface id in NFVO DB: %s" % iface_id
                     
                     return vnf_id
@@ -219,42 +227,46 @@ class nfvo_db(db_base.db_base):
                         #print "Internal vm id in NFVO DB: %s" % vm_id
                         vmDict[vm['name']] = vm_id
                      
-                    #Collect the data interfaces of each VM/VNFC under the 'numas' field
-                    dataifacesDict = {}
-                    for vm in vnf_descriptor['vnf']['VNFC']:
-                        dataifacesDict[vm['name']] = {}
-                        for numa in vm.get('numas', []):
-                            for dataiface in numa.get('interfaces',[]):
-                                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']]['bw'] = dataiface['bandwidth']
-                                dataifacesDict[vm['name']][dataiface['name']]['model'] = "PF" if dataiface['dedicated']=="yes" else ("VF"  if dataiface['dedicated']=="no" else "VFnotShared")
-                    
                     #Collect the bridge interfaces of each VM/VNFC under the 'bridge-ifaces' field
                     bridgeInterfacesDict = {}
                     for vm in vnf_descriptor['vnf']['VNFC']:
                         if 'bridge-ifaces' in  vm:
                             bridgeInterfacesDict[vm['name']] = {}
                             for bridgeiface in vm['bridge-ifaces']:
+                                created_time += 0.00001
                                 db_base._convert_bandwidth(bridgeiface, logger=self.logger)
                                 if 'port-security' in bridgeiface:
                                     bridgeiface['port_security'] = bridgeiface.pop('port-security')
                                 if 'floating-ip' in bridgeiface:
                                     bridgeiface['floating_ip'] = bridgeiface.pop('floating-ip')
-                                bridgeInterfacesDict[vm['name']][bridgeiface['name']] = {}
-                                bridgeInterfacesDict[vm['name']][bridgeiface['name']]['vpci'] = bridgeiface.get('vpci',None)
-                                bridgeInterfacesDict[vm['name']][bridgeiface['name']]['mac'] = bridgeiface.get('mac_address',None)
-                                bridgeInterfacesDict[vm['name']][bridgeiface['name']]['bw'] = bridgeiface.get('bandwidth', None)
-                                bridgeInterfacesDict[vm['name']][bridgeiface['name']]['model'] = bridgeiface.get('model', None)
-                                bridgeInterfacesDict[vm['name']][bridgeiface['name']]['port_security'] = \
-                                    int(bridgeiface.get('port_security', True))
-                                bridgeInterfacesDict[vm['name']][bridgeiface['name']]['floating_ip'] = \
-                                    int(bridgeiface.get('floating_ip', False))
-                    
+                                ifaceDict = {}
+                                ifaceDict['vpci'] = bridgeiface.get('vpci',None)
+                                ifaceDict['mac'] = bridgeiface.get('mac_address',None)
+                                ifaceDict['bw'] = bridgeiface.get('bandwidth', None)
+                                ifaceDict['model'] = bridgeiface.get('model', None)
+                                ifaceDict['port_security'] = int(bridgeiface.get('port_security', True))
+                                ifaceDict['floating_ip'] = int(bridgeiface.get('floating_ip', False))
+                                ifaceDict['created_time'] = created_time
+                                bridgeInterfacesDict[vm['name']][bridgeiface['name']] = ifaceDict
+
+                    # Collect the data interfaces of each VM/VNFC under the 'numas' field
+                    dataifacesDict = {}
+                    for vm in vnf_descriptor['vnf']['VNFC']:
+                        dataifacesDict[vm['name']] = {}
+                        for numa in vm.get('numas', []):
+                            for dataiface in numa.get('interfaces', []):
+                                created_time += 0.00001
+                                db_base._convert_bandwidth(dataiface, logger=self.logger)
+                                ifaceDict = {}
+                                ifaceDict['vpci'] = dataiface['vpci']
+                                ifaceDict['bw'] = dataiface['bandwidth']
+                                ifaceDict['model'] = "PF" if dataiface['dedicated'] == "yes" else \
+                                    ("VF" if dataiface['dedicated'] == "no" else "VFnotShared")
+                                ifaceDict['created_time'] = created_time
+                                dataifacesDict[vm['name']][dataiface['name']] = ifaceDict
+
                     #For each internal connection, we add it to the interfaceDict and we  create the appropriate net in the NFVO database.
                     #print "Adding new nets (VNF internal nets) to the NFVO database (if any)"
-                    internalconnList = []
                     if 'internal-connections' in vnf_descriptor['vnf']:
                         for net in vnf_descriptor['vnf']['internal-connections']:
                             #print "Net name: %s. Description: %s" % (net['name'], net['description'])
@@ -303,27 +315,22 @@ class nfvo_db(db_base.db_base):
                                 ifaceItem["type"] = net['type']
                                 ifaceItem["ip_address"] = element.get('ip_address',None)
                                 if ifaceItem ["type"] == "data":
-                                    ifaceItem["vpci"] =  dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci'] 
-                                    ifaceItem["bw"] =    dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw']
-                                    ifaceItem["model"] = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model']
+                                    ifaceDict = dataifacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+                                    ifaceItem["vpci"] =  ifaceDict['vpci']
+                                    ifaceItem["bw"] =    ifaceDict['bw']
+                                    ifaceItem["model"] = ifaceDict['model']
                                 else:
-                                    ifaceItem["vpci"] =  bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['vpci']
-                                    ifaceItem["mac"] =  bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['mac']
-                                    ifaceItem["bw"] =    bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['bw']
-                                    ifaceItem["model"] = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]['model']
-                                    ifaceItem["port_security"] = \
-                                    bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['port_security']
-                                    ifaceItem["floating_ip"] = \
-                                    bridgeInterfacesDict[element['VNFC']][element['local_iface_name']]['floating_ip']
-                                internalconnList.append(ifaceItem)
-                            #print "Internal net id in NFVO DB: %s" % net_id
-                    
-                    #print "Adding internal interfaces to the NFVO database (if any)"
-                    for iface in internalconnList:
-                        print "Iface name: %s" % iface['internal_name']
-                        created_time += 0.00001
-                        iface_id = self._new_row_internal('interfaces', iface, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
-                        #print "Iface id in NFVO DB: %s" % iface_id
+                                    ifaceDict = bridgeInterfacesDict[ element['VNFC'] ][ element['local_iface_name'] ]
+                                    ifaceItem["vpci"] =  ifaceDict['vpci']
+                                    ifaceItem["mac"] =  ifaceDict['mac']
+                                    ifaceItem["bw"] =    ifaceDict['bw']
+                                    ifaceItem["model"] = ifaceDict['model']
+                                    ifaceItem["port_security"] = ifaceDict['port_security']
+                                    ifaceItem["floating_ip"] = ifaceDict['floating_ip']
+                                created_time_iface = ifaceDict["created_time"]
+                                #print "Iface name: %s" % iface['internal_name']
+                                iface_id = self._new_row_internal('interfaces', ifaceItem, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface)
+                                #print "Iface id in NFVO DB: %s" % iface_id
                     
                     #print "Adding external interfaces to the NFVO database"
                     for iface in vnf_descriptor['vnf']['external-connections']:
@@ -338,6 +345,7 @@ class nfvo_db(db_base.db_base):
                             myIfaceDict["vpci"]  = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
                             myIfaceDict["bw"]    = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
                             myIfaceDict["model"] = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['model']
+                            created_time_iface = dataifacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['created_time']
                         else:
                             myIfaceDict["vpci"]  = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['vpci']
                             myIfaceDict["bw"]    = bridgeInterfacesDict[ iface['VNFC'] ][ iface['local_iface_name'] ]['bw']
@@ -347,9 +355,9 @@ class nfvo_db(db_base.db_base):
                                 bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['port_security']
                             myIfaceDict["floating_ip"] = \
                                 bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['floating_ip']
-                        print "Iface name: %s" % iface['name']
-                        created_time += 0.00001
-                        iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time)
+                            created_time_iface = bridgeInterfacesDict[iface['VNFC']][iface['local_iface_name']]['created_time']
+                        #print "Iface name: %s" % iface['name']
+                        iface_id = self._new_row_internal('interfaces', myIfaceDict, add_uuid=True, root_uuid=vnf_id, created_time=created_time_iface)
                         #print "Iface id in NFVO DB: %s" % iface_id
                     
                     return vnf_id
@@ -953,9 +961,10 @@ class nfvo_db(db_base.db_base):
                         for vm in vnf['vms']:
                             vm_manage_iface_list=[]
                             #instance_interfaces
-                            cmd = "SELECT vim_interface_id, instance_net_id, internal_name,external_name, mac_address, ii.ip_address as ip_address, vim_info, i.type as type "\
-                                    " FROM instance_interfaces as ii join interfaces as i on ii.interface_id=i.uuid "\
-                                    " WHERE instance_vm_id='{}' ORDER BY created_at".format(vm['uuid'])
+                            cmd = "SELECT vim_interface_id, instance_net_id, internal_name,external_name, mac_address,"\
+                                  " ii.ip_address as ip_address, vim_info, i.type as type"\
+                                  " FROM instance_interfaces as ii join interfaces as i on ii.interface_id=i.uuid"\
+                                  " WHERE instance_vm_id='{}' ORDER BY created_at".format(vm['uuid'])
                             self.logger.debug(cmd)
                             self.cur.execute(cmd )
                             vm['interfaces'] = self.cur.fetchall()
index ab492f6..c34d831 100755 (executable)
--- a/openmano
+++ b/openmano
@@ -28,7 +28,7 @@ openmano client used to interact with openmano-server (openmanod)
 '''
 __author__="Alfonso Tierno, Gerardo Garcia"
 __date__ ="$09-oct-2014 09:09:48$"
-__version__="0.4.9-r515"
+__version__="0.4.11-r517"
 version_date="Jan 2017"
 
 from argcomplete.completers import FilesCompleter
@@ -127,20 +127,25 @@ def _print_verbose(mano_response, verbose_level=0):
                 uuid = content['id']
             elif "vim_id" in content:
                 uuid = content['vim_id']
-            myoutput = "%s %s" %(uuid.ljust(38),content['name'].ljust(20))
-            if "status" in content:
+            name = content.get('name');
+            if not uuid:
+                uuid = ""
+            if not name:
+                name = ""
+            myoutput = "%s %s" %(uuid.ljust(38),name.ljust(20))
+            if content.get("status"):
                 myoutput += " " + content['status'].ljust(20)
             elif "enabled" in content and not content["enabled"]:
                 myoutput += " enabled=False".ljust(20)
             if verbose_level >=1:
-                if 'created_at' in content:
+                if content.get('created_at'):
                     myoutput += " " + content['created_at'].ljust(20)
                 if verbose_level >=2:
                     new_line='\n'
-                    if 'type' in content and content['type']!=None:
+                    if content.get('type'):
                         myoutput += new_line + "  Type: " + content['type'].ljust(29)
                         new_line=''
-                    if 'description' in content and content['description']!=None:
+                    if content.get('description'):
                         myoutput += new_line + "  Description: " + content['description'].ljust(20)
             print myoutput
     else:
@@ -357,7 +362,7 @@ def vnf_list(args):
                 print "    External interfaces:"
                 for interface in vnf['external-connections']:
                     print "        %s %s %s %s" %(interface['external_name'].ljust(20), interface['vm_name'].ljust(20), interface['internal_name'].ljust(20), \
-                                                  interface['vpci'].ljust(14))
+                                                  interface.get('vpci',"").ljust(14))
     else:
         print content['error']['description']
         if args.verbose:
@@ -1415,7 +1420,7 @@ if __name__=="__main__":
             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)
     
-    for item in ("network", "tenant"):
+    for item in ("network", "tenant", "image"):
         if item=="network":
             commnad_name = 'vim-net'
         else:
@@ -1430,17 +1435,18 @@ if __name__=="__main__":
         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")
 
-        vim_item_create_parser = subparsers.add_parser(commnad_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")
-        if item=="network":
-            vim_item_create_parser.add_argument("--type", action="store", help="type of network, data, ptp, bridge")
-            vim_item_create_parser.add_argument("--shared", action="store_true", help="Private or shared")
-            vim_item_create_parser.add_argument("--bind-net", action="store", help="For openvim datacenter type, net to be bind to, for vlan type, use sufix ':<vlan_tag>'")
-        else:
-            vim_item_create_parser.add_argument("--description", action="store", help="description of the %s" % item)
-        vim_item_create_parser.set_defaults(func=vim_action, item=item, action="create")
+        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.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")
+            if item=="network":
+                vim_item_create_parser.add_argument("--type", action="store", help="type of network, data, ptp, bridge")
+                vim_item_create_parser.add_argument("--shared", action="store_true", help="Private or shared")
+                vim_item_create_parser.add_argument("--bind-net", action="store", help="For openvim datacenter type, net to be bind to, for vlan type, use sufix ':<vlan_tag>'")
+            else:
+                vim_item_create_parser.add_argument("--description", action="store", help="description of the %s" % item)
+            vim_item_create_parser.set_defaults(func=vim_action, item=item, action="create")
 
     argcomplete.autocomplete(main_parser)
     
index c325b00..1ea64f6 100644 (file)
@@ -80,6 +80,7 @@ config_schema = {
         "http_port": port_schema,
         "http_admin_port": port_schema,
         "http_host": nameshort_schema,
+        "auto_push_VNF_to_VIMs": {"type":"boolean"},
         "vnf_repository": path_schema,
         "db_host": nameshort_schema,
         "db_user": nameshort_schema,
@@ -107,10 +108,12 @@ config_schema = {
         "log_level_vim": log_level_schema,
         "log_level_nfvo": log_level_schema,
         "log_level_http": log_level_schema,
+        "log_level_console": log_level_schema,
         "log_file_db": path_schema,
         "log_file_vim": path_schema,
         "log_file_nfvo": path_schema,
         "log_file_http": path_schema,
+        "log_file_console": path_schema,
         "log_socket_host": nameshort_schema,
         "log_socket_port": port_schema,
         "log_file": path_schema,
index 2d2a9cc..ebcee2b 100644 (file)
@@ -48,8 +48,11 @@ db_name:   mano_db            # Name of the MANO DB
 #other MANO parameters
 #  Folder where the VNF descriptors will be stored
 #  The folder will be created in the execution folder if it does not exist
-#vnf_repository: "./vnfrepo"   # Use an absolute path to avoid misunderstandings 
+#vnf_repository: "./vnfrepo"  # Use an absolute path to avoid misunderstandings
 
+#   Indicates if at VNF onboarding, flavors and images are loaded at all related VIMs,
+#   in order to speed up the later instantiation.
+auto_push_VNF_to_VIMs: False  # by default True
 
 #general logging parameters 
    #choose among: DEBUG, INFO, WARNING, ERROR, CRITICAL
@@ -63,9 +66,11 @@ log_level:         DEBUG  #general log levels for internal logging
 #log_level_vim:     DEBUG  #VIM connection log levels
 #log_file_vim:      /opt/openmano/logs/openmano_vimconn.log
 #log_level_nfvo:    DEBUG  #Main engine log levels
-#log_file_nfvo:      /opt/openmano/logs/openmano_nfvo.log
+#log_file_nfvo:     /opt/openmano/logs/openmano_nfvo.log
 #log_level_http:    DEBUG  #Main engine log levels
-#log_file_http:      /opt/openmano/logs/openmano_http.log
+#log_file_http:     /opt/openmano/logs/openmano_http.log
+#log_level_console: DEBUG  #proxy console log levels
+#log_file_console:  /opt/openmano/logs/openmano_console.log
 
 #Uncomment to send logs via IP to an external host
 #log_socket_host:   localhost
index 3d0feb4..5ec3650 100755 (executable)
@@ -33,7 +33,7 @@ It loads the configuration file and launches the http_server thread that will li
 '''
 __author__="Alfonso Tierno, Gerardo Garcia, Pablo Montes"
 __date__ ="$26-aug-2014 11:09:29$"
-__version__="0.5.5-r514"
+__version__="0.5.8-r518"
 version_date="Jan 2017"
 database_version="0.19"      #expected database schema version
 
@@ -64,6 +64,7 @@ def load_configuration(configuration_file):
                      'http_console_host': None,
                      'log_level': 'DEBUG',
                      'log_socket_port': 9022,
+                     'auto_push_VNF_to_VIMs': True
                     }
     try:
         #Check config file exists
@@ -240,7 +241,7 @@ if __name__=="__main__":
         logger.critical("Starting openmano server version: '%s %s' command: '%s'",  
                          __version__, version_date, " ".join(sys.argv))
         
-        for log_module in ("nfvo", "http", "vim", "db"):
+        for log_module in ("nfvo", "http", "vim", "db", "console"):
             log_level_module = "log_level_" + log_module
             log_file_module = "log_file_" + log_module
             logger_module = logging.getLogger('openmano.' + log_module)
index 814be65..ad06dc8 100644 (file)
@@ -242,7 +242,13 @@ class vimconnector():
             Returns the flavor dict details {'id':<>, 'name':<>, other vim specific } #TODO to concrete
         '''
         raise vimconnNotImplemented( "Should have implemented this" )
-        
+
+    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
+        """
+        raise vimconnNotImplemented( "Should have implemented this" )
+
     def new_flavor(self, flavor_data):
         '''Adds a tenant flavor to VIM
             flavor_data contains a dictionary with information, keys:
@@ -287,7 +293,9 @@ class vimconnector():
         raise vimconnNotImplemented( "Should have implemented this" )
 
     def get_image_id_from_path(self, path):
-        '''Get the image id from image path in the VIM database. Returns the image_id'''
+        """Get the image id from image path in the VIM database.
+           Returns the image_id or raises a vimconnNotFoundException
+        """
         raise vimconnNotImplemented( "Should have implemented this" )
         
     def get_image_list(self, filter_dict={}):
index 0cd9b19..80ea09c 100644 (file)
@@ -34,6 +34,7 @@ import logging
 import netaddr
 import time
 import yaml
+import random
 
 from novaclient import client as nClient_v2, exceptions as nvExceptions, api_versions as APIVersion
 import keystoneclient.v2_0.client as ksClient_v2
@@ -78,6 +79,9 @@ class vimconnector(vimconn.vimconnector):
         
         self.k_creds={}
         self.n_creds={}
+        if self.config.get("insecure"):
+            self.k_creds["insecure"] = True
+            self.n_creds["insecure"] = True
         if not url:
             raise TypeError, 'url param can not be NoneType'
         self.k_creds['auth_url'] = url
@@ -302,8 +306,9 @@ class vimconnector(vimconn.vimconnector):
             if not ip_profile:
                 ip_profile = {}
             if 'subnet_address' not in ip_profile:
-                #Fake subnet is required 
-                ip_profile['subnet_address'] = "192.168.111.0/24"
+                #Fake subnet is required
+                subnet_rand = random.randint(0, 255)
+                ip_profile['subnet_address'] = "192.168.{}.0/24".format(subnet_rand)
             if 'ip_version' not in ip_profile: 
                 ip_profile['ip_version'] = "IPv4"
             subnet={"name":net_name+"-subnet",
@@ -459,6 +464,38 @@ class vimconnector(vimconn.vimconnector):
         except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException, ConnectionError) as e:
             self._format_exception(e)
 
+    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
+        """
+        try:
+            self._reload_connection()
+            numa=None
+            numas = flavor_dict.get("extended",{}).get("numas")
+            if numas:
+                #TODO
+                raise vimconn.vimconnNotFoundException("Flavor with EPA still not implemted")
+                # if len(numas) > 1:
+                #     raise vimconn.vimconnNotFoundException("Cannot find any flavor with more than one numa")
+                # numa=numas[0]
+                # numas = extended.get("numas")
+            for flavor in self.nova.flavors.list():
+                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
+            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)
+
+
     def new_flavor(self, flavor_data, change_name_if_used=True):
         '''Adds a tenant flavor to openstack VIM
         if change_name_if_used is True, it will change name in case of conflict, because it is not supported name repetition
@@ -656,9 +693,9 @@ class vimconnector(vimconn.vimconnector):
             #Then we filter by the rest of filter fields: checksum
             filtered_list = []
             for image in image_list:
-                image_dict=self.glance.images.get(image.id)
-                if 'checksum' not in filter_dict or image_dict['checksum']==filter_dict.get('checksum'):
-                    filtered_list.append(image_dict)
+                image_class=self.glance.images.get(image.id)
+                if 'checksum' not in filter_dict or image_class['checksum']==filter_dict.get('checksum'):
+                    filtered_list.append(image_class.copy())
             return filtered_list
         except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
             self._format_exception(e)
@@ -681,7 +718,7 @@ class vimconnector(vimconn.vimconnector):
                 #TODO ip, security groups
         Returns the instance identifier
         '''
-        self.logger.debug("Creating VM image '%s' flavor '%s' nics='%s'",image_id, flavor_id,str(net_list))
+        self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'",image_id, flavor_id,str(net_list))
         try:
             metadata={}
             net_list_vim=[]
@@ -721,8 +758,12 @@ class vimconnector(vimconn.vimconnector):
                     self.logger.warn("new_vminstance: Warning, can not connect a passthrough interface ")
                     #TODO insert this when openstack consider passthrough ports as openstack neutron ports
                 if net.get('floating_ip', False):
+                    net['exit_on_floating_ip_error'] = True
                     external_network.append(net)
-                 
+                elif net['use'] == 'mgmt' and self.config.get('use_floating_ip'):
+                    net['exit_on_floating_ip_error'] = False
+                    external_network.append(net)
+
             if metadata_vpci:
                 metadata = {"pci_assignement": json.dumps(metadata_vpci)}
                 if len(metadata["pci_assignement"]) >255:
@@ -754,7 +795,7 @@ class vimconnector(vimconn.vimconnector):
                         userdata_dict["ssh-authorized-keys"] = cloud_config["key-pairs"]
                         userdata_dict["users"] = [{"default": None, "ssh-authorized-keys": cloud_config["key-pairs"] }]
                     if cloud_config.get("users"):
-                        if "users" not in cloud_config:
+                        if "users" not in userdata_dict:
                             userdata_dict["users"] = [ "default" ]
                         for user in cloud_config["users"]:
                             user_info = {
@@ -840,71 +881,72 @@ class vimconnector(vimconn.vimconnector):
             pool_id = None
             floating_ips = self.neutron.list_floatingips().get("floatingips", ())
             for floating_network in external_network:
-                # wait until vm is active
-                elapsed_time = 0
-                while elapsed_time < server_timeout:
-                    status = self.nova.servers.get(server.id).status
-                    if status == 'ACTIVE':
-                        break
-                    time.sleep(1)
-                    elapsed_time += 1
+                try:
+                    # wait until vm is active
+                    elapsed_time = 0
+                    while elapsed_time < server_timeout:
+                        status = self.nova.servers.get(server.id).status
+                        if status == 'ACTIVE':
+                            break
+                        time.sleep(1)
+                        elapsed_time += 1
 
-                #if we exceeded the timeout rollback
-                if elapsed_time >= server_timeout:
-                    self.delete_vminstance(server.id)
-                    raise vimconn.vimconnException('Timeout creating instance ' + name,
-                                                   http_code=vimconn.HTTP_Request_Timeout)
+                    #if we exceeded the timeout rollback
+                    if elapsed_time >= server_timeout:
+                        raise vimconn.vimconnException('Timeout creating instance ' + name,
+                                                       http_code=vimconn.HTTP_Request_Timeout)
+
+                    assigned = False
+                    while(assigned == False):
+                        if floating_ips:
+                            ip = floating_ips.pop(0)
+                            if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
+                                free_floating_ip = ip.get("floating_ip_address")
+                                try:
+                                    fix_ip = floating_network.get('ip')
+                                    server.add_floating_ip(free_floating_ip, fix_ip)
+                                    assigned = True
+                                except Exception as e:
+                                    raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+  str(e), http_code=vimconn.HTTP_Conflict)
+                        else:
+                            #Find the external network
+                            external_nets = list()
+                            for net in self.neutron.list_networks()['networks']:
+                                if net['router:external']:
+                                        external_nets.append(net)
 
-                assigned = False
-                while(assigned == False):
-                    if floating_ips:
-                        ip = floating_ips.pop(0)
-                        if not ip.get("port_id", False) and ip.get('tenant_id') == server.tenant_id:
-                            free_floating_ip = ip.get("floating_ip_address")
+                            if len(external_nets) == 0:
+                                raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
+                                                               "network is present",
+                                                                http_code=vimconn.HTTP_Conflict)
+                            if len(external_nets) > 1:
+                                raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
+                                                               "external networks are present",
+                                                               http_code=vimconn.HTTP_Conflict)
+
+                            pool_id = external_nets[0].get('id')
+                            param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
                             try:
+                                #self.logger.debug("Creating floating IP")
+                                new_floating_ip = self.neutron.create_floatingip(param)
+                                free_floating_ip = new_floating_ip['floatingip']['floating_ip_address']
                                 fix_ip = floating_network.get('ip')
                                 server.add_floating_ip(free_floating_ip, fix_ip)
-                                assigned = True
+                                assigned=True
                             except Exception as e:
-                                self.delete_vminstance(server.id)
-                                raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+  str(e), http_code=vimconn.HTTP_Conflict)
-                    else:
-                        #Find the external network
-                        external_nets = list()
-                        for net in self.neutron.list_networks()['networks']:
-                            if net['router:external']:
-                                    external_nets.append(net)
-
-                        if len(external_nets) == 0:
-                            self.delete_vminstance(server.id)
-                            raise vimconn.vimconnException("Cannot create floating_ip automatically since no external "
-                                                           "network is present",
-                                                            http_code=vimconn.HTTP_Conflict)
-                        if len(external_nets) > 1:
-                            self.delete_vminstance(server.id)
-                            raise vimconn.vimconnException("Cannot create floating_ip automatically since multiple "
-                                                           "external networks are present",
-                                                           http_code=vimconn.HTTP_Conflict)
+                                raise vimconn.vimconnException(type(e).__name__ + ": Cannot assign floating_ip "+  str(e), http_code=vimconn.HTTP_Conflict)
+                except Exception as e:
+                    if not floating_network['exit_on_floating_ip_error']:
+                        self.logger.warn("Cannot create floating_ip. %s", str(e))
+                        continue
+                    self.delete_vminstance(server.id)
+                    raise
 
-                        pool_id = external_nets[0].get('id')
-                        param = {'floatingip': {'floating_network_id': pool_id, 'tenant_id': server.tenant_id}}
-                        try:
-                            #self.logger.debug("Creating floating IP")
-                            new_floating_ip = self.neutron.create_floatingip(param)
-                            free_floating_ip = new_floating_ip['floatingip']['floating_ip_address']
-                            fix_ip = floating_network.get('ip')
-                            server.add_floating_ip(free_floating_ip, fix_ip)
-                            assigned=True
-                        except Exception as e:
-                            self.delete_vminstance(server.id)
-                            raise vimconn.vimconnException(type(e).__name__ + ": Cannot create floating_ip "+  str(e), http_code=vimconn.HTTP_Conflict)
-                
             return server.id
 #        except nvExceptions.NotFound as e:
 #            error_value=-vimconn.HTTP_Not_Found
 #            error_text= "vm instance %s not found" % vm_id
-        except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError
-                ) as e:
+        except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
             # delete the volumes we just created
             if block_device_mapping != None:
                 for volume_id in block_device_mapping.itervalues():
index bd963c7..fb59ecc 100644 (file)
@@ -795,6 +795,7 @@ class vimconnector(vimconn.vimconnector):
                 #TODO ip, security groups
         Returns the instance identifier
         '''
+        self.logger.debug("new_vminstance input: image='%s' flavor='%s' nics='%s'", image_id, flavor_id, str(net_list))
         try:
             self._get_my_tenant()
 #            net_list = []
index 19382d6..3e81f51 100644 (file)
@@ -177,6 +177,9 @@ class vimconnector(vimconn.vimconnector):
         self.admin_password = None
         self.admin_user = None
         self.org_name = ""
+        self.nsx_manager = None
+        self.nsx_user = None
+        self.nsx_password = None
 
         if tenant_name is not None:
             orgnameandtenant = tenant_name.split(":")
@@ -197,6 +200,13 @@ class vimconnector(vimconn.vimconnector):
         except KeyError:
             raise vimconn.vimconnException(message="Error admin username or admin password is empty.")
 
+        try:
+            self.nsx_manager = config['nsx_manager']
+            self.nsx_user = config['nsx_user']
+            self.nsx_password = config['nsx_password']
+        except KeyError:
+            raise vimconn.vimconnException(message="Error: nsx manager or nsx user or nsx password is empty in Config")
+
         self.org_uuid = None
         self.vca = None
 
@@ -1072,6 +1082,43 @@ class vimconnector(vimconn.vimconnector):
 
         return self.get_catalogid(catalog_md5_name, vca.get_catalogs())
 
+    def get_image_list(self, filter_dict={}):
+        '''Obtain tenant images from VIM
+        Filter_dict can be:
+            name: image name
+            id: image uuid
+            checksum: image checksum
+            location: image path
+        Returns the image list of dictionaries:
+            [{<the fields at Filter_dict plus some VIM specific>}, ...]
+            List can be empty
+        '''
+        vca = self.connect()
+        if not vca:
+            raise vimconn.vimconnConnectionException("self.connect() is failed.")
+        try:
+            image_list = []
+            catalogs = vca.get_catalogs()
+            if len(catalogs) == 0:
+                return image_list
+            else:
+                for catalog in catalogs:
+                    catalog_uuid = catalog.get_id().split(":")[3]
+                    name = catalog.name
+                    filtered_dict = {}
+                    if filter_dict.get("name") and filter_dict["name"] != name:
+                        continue
+                    if filter_dict.get("id") and filter_dict["id"] != catalog_uuid:
+                        continue
+                    filtered_dict ["name"] = name
+                    filtered_dict ["id"] = catalog_uuid
+                    image_list.append(filtered_dict)
+
+                self.logger.debug("List of already created catalog items: {}".format(image_list))
+                return image_list
+        except Exception as exp:
+            raise vimconn.vimconnException("Exception occured while retriving catalog items {}".format(exp))
+
     def get_vappid(self, vdc=None, vapp_name=None):
         """ Method takes vdc object and vApp name and returns vapp uuid or None
 
@@ -1615,6 +1662,40 @@ class vimconnector(vimconn.vimconnector):
         """
 
         self.logger.debug("Client requesting refresh vm status for {} ".format(vm_list))
+
+        mac_ip_addr={}
+        rheaders = {'Content-Type': 'application/xml'}
+        iso_edges = ['edge-2','edge-3','edge-6','edge-7','edge-8','edge-9','edge-10']
+
+        try:
+            for edge in iso_edges:
+                nsx_api_url = '/api/4.0/edges/'+ edge +'/dhcp/leaseInfo'
+                self.logger.debug("refresh_vms_status: NSX Manager url: {}".format(nsx_api_url))
+
+                resp = requests.get(self.nsx_manager + nsx_api_url,
+                                    auth = (self.nsx_user, self.nsx_password),
+                                    verify = False, headers = rheaders)
+
+                if resp.status_code == requests.codes.ok:
+                    dhcp_leases = XmlElementTree.fromstring(resp.text)
+                    for child in dhcp_leases:
+                        if child.tag == 'dhcpLeaseInfo':
+                            dhcpLeaseInfo = child
+                            for leaseInfo in dhcpLeaseInfo:
+                                for elem in leaseInfo:
+                                    if (elem.tag)=='macAddress':
+                                        mac_addr = elem.text
+                                    if (elem.tag)=='ipAddress':
+                                        ip_addr = elem.text
+                                if (mac_addr) is not None:
+                                    mac_ip_addr[mac_addr]= ip_addr
+                    self.logger.debug("NSX Manager DHCP Lease info: mac_ip_addr : {}".format(mac_ip_addr))
+                else:
+                    self.logger.debug("Error occurred while getting DHCP lease info from NSX Manager: {}".format(resp.content))
+        except KeyError:
+            self.logger.debug("Error in response from NSX Manager {}".format(KeyError.message))
+            self.logger.debug(traceback.format_exc())
+
         vca = self.connect()
         if not vca:
             raise vimconn.vimconnConnectionException("self.connect() is failed.")
@@ -1644,6 +1725,10 @@ class vimconnector(vimconn.vimconnector):
                     for vapp_network in vm_app_networks:
                         for vm_network in vapp_network:
                             if vm_network['name'] == vmname:
+                                #Assign IP Address based on MAC Address in NSX DHCP lease info
+                                for mac_adres,ip_adres in mac_ip_addr.iteritems():
+                                    if mac_adres == vm_network['mac']:
+                                        vm_network['ip']=ip_adres
                                 interface = {"mac_address": vm_network['mac'],
                                              "vim_net_id": self.get_network_id_by_name(vm_network['network_name']),
                                              "vim_interface_id": self.get_network_id_by_name(vm_network['network_name']),
@@ -1685,15 +1770,30 @@ class vimconnector(vimconn.vimconnector):
             the_vapp = vca.get_vapp(vdc, vapp_name)
             # TODO fix all status
             if "start" in action_dict:
-                if action_dict["start"] == "rebuild":
-                    the_vapp.deploy(powerOn=True)
+                vm_info = the_vapp.get_vms_details()
+                vm_status = vm_info[0]['status']
+                self.logger.info("Power on vApp: vm_status:{} {}".format(type(vm_status),vm_status))
+                if vm_status == "Suspended" or vm_status == "Powered off":
+                    power_on_task = the_vapp.poweron()
+                    if power_on_task is not None and type(power_on_task) is GenericTask:
+                        result = vca.block_until_completed(power_on_task)
+                        if result:
+                            self.logger.info("action_vminstance: Powered on vApp: {}".format(vapp_name))
+                        else:
+                            self.logger.info("action_vminstance: Failed to power on vApp: {}".format(vapp_name))
+                    else:
+                        self.logger.info("action_vminstance: Wait for vApp {} to power on".format(vapp_name))
+            elif "rebuild" in action_dict:
+                self.logger.info("action_vminstance: Rebuilding vApp: {}".format(vapp_name))
+                power_on_task = the_vapp.deploy(powerOn=True)
+                if type(power_on_task) is GenericTask:
+                    result = vca.block_until_completed(power_on_task)
+                    if result:
+                        self.logger.info("action_vminstance: Rebuilt vApp: {}".format(vapp_name))
+                    else:
+                        self.logger.info("action_vminstance: Failed to rebuild vApp: {}".format(vapp_name))
                 else:
-                    vm_info = the_vapp.get_vms_details()
-                    vm_status = vm_info[0]['status']
-                    if vm_status == "Suspended":
-                        the_vapp.poweron()
-                    elif vm_status.status == "Powered off":
-                        the_vapp.poweron()
+                    self.logger.info("action_vminstance: Wait for vApp rebuild {} to power on".format(vapp_name))
             elif "pause" in action_dict:
                 pass
                 ## server.pause()
@@ -1701,7 +1801,15 @@ class vimconnector(vimconn.vimconnector):
                 pass
                 ## server.resume()
             elif "shutoff" in action_dict or "shutdown" in action_dict:
-                the_vapp.shutdown()
+                power_off_task = the_vapp.undeploy(action='powerOff')
+                if type(power_off_task) is GenericTask:
+                    result = vca.block_until_completed(power_off_task)
+                    if result:
+                        self.logger.info("action_vminstance: Powered off vApp: {}".format(vapp_name))
+                    else:
+                        self.logger.info("action_vminstance: Failed to power off vApp: {}".format(vapp_name))
+                else:
+                    self.logger.info("action_vminstance: Wait for vApp {} to power off".format(vapp_name))
             elif "forceOff" in action_dict:
                 the_vapp.reset()
             elif "terminate" in action_dict:
index ce8c5de..92e14fb 100644 (file)
@@ -26,7 +26,7 @@ vnf:
     description: Single-VM VNF with a traditional cloud VM based on generic Linux OS
     external-connections:
     -   name:              eth0
-        type:              bridge
+        type:              mgmt
         description:       General purpose interface
         VNFC:              linux-VM
         local_iface_name:  eth0