Merge "v0.4.61 fixed 37 Deploying multisite get properly the information from sites...
[osm/RO.git] / vimconn_openstack.py
index 6e5f007..e0a3aa3 100644 (file)
@@ -31,6 +31,7 @@ import vimconn
 import json
 import yaml
 import logging
+import netaddr
 
 from novaclient import client as nClient, exceptions as nvExceptions
 import keystoneclient.v2_0.client as ksClient
@@ -55,7 +56,7 @@ netStatus2manoFormat={'ACTIVE':'ACTIVE','PAUSED':'PAUSED','INACTIVE':'INACTIVE',
                      }
 
 class vimconnector(vimconn.vimconnector):
-    def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level="DEBUG", config={}):
+    def __init__(self, uuid, name, tenant_id, tenant_name, url, url_admin=None, user=None, passwd=None, log_level=None, config={}):
         '''using common constructor parameters. In this case 
         'url' is the keystone authorization url,
         'url_admin' is not use
@@ -81,7 +82,9 @@ class vimconnector(vimconn.vimconnector):
             self.k_creds['password'] = passwd
             self.n_creds['api_key']  = passwd
         self.reload_client       = True
-        self.logger = logging.getLogger('mano.vim.openstack')
+        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 
@@ -171,8 +174,8 @@ class vimconnector(vimconn.vimconnector):
     def _format_exception(self, exception):
         '''Transform a keystone, nova, neutron  exception into a vimconn exception'''
         if isinstance(exception, (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError,
-                                  ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed,
-                                  neClient.exceptions.ConnectionFailed)):
+                                  ConnectionError, ksExceptions.ConnectionError, neExceptions.ConnectionFailed
+                                  )):
             raise vimconn.vimconnConnectionException(type(exception).__name__ + ": " + str(exception))            
         elif isinstance(exception, (nvExceptions.ClientException, ksExceptions.ClientException, 
                                     neExceptions.NeutronException, nvExceptions.BadRequest)):
@@ -200,7 +203,7 @@ class vimconnector(vimconn.vimconnector):
             for tenant in tenant_class_list:
                 tenant_list.append(tenant.to_dict())
             return tenant_list
-        except (ksExceptions.ConnectionError, ksExceptions.ClientException)  as e:
+        except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError)  as e:
             self._format_exception(e)
 
     def new_tenant(self, tenant_name, tenant_description):
@@ -210,7 +213,7 @@ class vimconnector(vimconn.vimconnector):
             self._reload_connection()
             tenant=self.keystone.tenants.create(tenant_name, tenant_description)
             return tenant.id
-        except (ksExceptions.ConnectionError, ksExceptions.ClientException)  as e:
+        except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError)  as e:
             self._format_exception(e)
 
     def delete_tenant(self, tenant_id):
@@ -220,13 +223,15 @@ class vimconnector(vimconn.vimconnector):
             self._reload_connection()
             self.keystone.tenants.delete(tenant_id)
             return tenant_id
-        except (ksExceptions.ConnectionError, ksExceptions.ClientException)  as e:
+        except (ksExceptions.ConnectionError, ksExceptions.ClientException, ConnectionError)  as e:
             self._format_exception(e)
         
-    def new_network(self,net_name,net_type, shared=False, cidr=None, vlan=None):
+    def new_network(self,net_name, net_type, ip_profile=None, shared=False, vlan=None):
         '''Adds a tenant network to VIM. Returns the network identifier'''
         self.logger.debug("Adding a new network to VIM name '%s', type '%s'", net_name, net_type)
+        #self.logger.debug(">>>>>>>>>>>>>>>>>> IP profile %s", str(ip_profile))
         try:
+            new_net = None
             self._reload_connection()
             network_dict = {'name': net_name, 'admin_state_up': True}
             if net_type=="data" or net_type=="ptp":
@@ -239,17 +244,44 @@ class vimconnector(vimconn.vimconnector):
             network_dict["shared"]=shared
             new_net=self.neutron.create_network({'network':network_dict})
             #print new_net
-            #create fake subnetwork
-            if not cidr:
-                cidr="192.168.111.0/24"
+            #create subnetwork, even if there is no profile
+            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"
+            if 'ip_version' not in ip_profile: 
+                ip_profile['ip_version'] = "IPv4"
             subnet={"name":net_name+"-subnet",
                     "network_id": new_net["network"]["id"],
-                    "ip_version": 4,
-                    "cidr": cidr
+                    "ip_version": 4 if ip_profile['ip_version']=="IPv4" else 6,
+                    "cidr": ip_profile['subnet_address']
                     }
+            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'])
+            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:
+                subnet['allocation_pools']=[]
+                subnet['allocation_pools'].append(dict())
+                subnet['allocation_pools'][0]['start'] = ip_profile['dhcp_start_address']
+            if 'dhcp_count' in ip_profile:
+                #parts = ip_profile['dhcp_start_address'].split('.')
+                #ip_int = (int(parts[0]) << 24) + (int(parts[1]) << 16) + (int(parts[2]) << 8) + int(parts[3])
+                ip_int = int(netaddr.IPAddress(ip_profile['dhcp_start_address']))
+                ip_int += ip_profile['dhcp_count'] - 1
+                ip_str = str(netaddr.IPAddress(ip_int))
+                subnet['allocation_pools'][0]['end'] = ip_str
+            #self.logger.debug(">>>>>>>>>>>>>>>>>> Subnet: %s", str(subnet))
             self.neutron.create_subnet({"subnet": subnet} )
             return new_net["network"]["id"]
-        except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e:
+        except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
+            if new_net:
+                self.neutron.delete_network(new_net['network']['id'])
             self._format_exception(e)
 
     def get_network_list(self, filter_dict={}):
@@ -270,7 +302,7 @@ class vimconnector(vimconn.vimconnector):
             net_list=net_dict["networks"]
             self.__net_os2mano(net_list)
             return net_list
-        except (neExceptions.ConnectionFailed, neClient.exceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e:
+        except (neExceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
             self._format_exception(e)
 
     def get_network(self, net_id):
@@ -310,7 +342,7 @@ class vimconnector(vimconn.vimconnector):
             self.neutron.delete_network(net_id)
             return net_id
         except (neExceptions.ConnectionFailed, neExceptions.NetworkNotFoundClient, neExceptions.NeutronException,
-                neClient.exceptions.ConnectionFailed, ksExceptions.ClientException, neExceptions.NeutronException) as e:
+                ksExceptions.ClientException, neExceptions.NeutronException, ConnectionError) as e:
             self._format_exception(e)
 
     def refresh_nets_status(self, net_list):
@@ -341,9 +373,12 @@ class vimconnector(vimconn.vimconnector):
                     net["status"] = "OTHER"
                     net["error_msg"] = "VIM status reported " + net_vim['status']
                     
-                if net['status'] == "ACIVE" and not net_vim['admin_state_up']:
+                if net['status'] == "ACTIVE" and not net_vim['admin_state_up']:
                     net['status'] = 'DOWN'
-                net['vim_info']  = yaml.safe_dump(net_vim)
+                try:
+                    net['vim_info'] = yaml.safe_dump(net_vim, default_flow_style=True, width=256)
+                except yaml.representer.RepresenterError:
+                    net['vim_info'] = str(net_vim)
                 if net_vim.get('fault'):  #TODO
                     net['error_msg'] = str(net_vim['fault'])
             except vimconn.vimconnNotFoundException as e:
@@ -365,7 +400,7 @@ class vimconnector(vimconn.vimconnector):
             flavor = self.nova.flavors.find(id=flavor_id)
             #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
             return flavor.to_dict()
-        except (nvExceptions.NotFound, nvExceptions.ClientException, ksExceptions.ClientException) as e:
+        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):
@@ -451,7 +486,7 @@ class vimconnector(vimconn.vimconnector):
             self.nova.flavors.delete(flavor_id)
             return flavor_id
         #except nvExceptions.BadRequest as e:
-        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException) as e:
+        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
             self._format_exception(e)
 
     def new_image(self,image_dict):
@@ -513,7 +548,7 @@ class vimconnector(vimconn.vimconnector):
                 return new_image.id
             except (nvExceptions.Conflict, ksExceptions.ClientException, nvExceptions.ClientException) as e:
                 self._format_exception(e)
-            except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError) as e:
+            except (HTTPException, gl1Exceptions.HTTPException, gl1Exceptions.CommunicationError, ConnectionError) as e:
                 if retry==max_retries:
                     continue
                 self._format_exception(e)
@@ -528,12 +563,11 @@ class vimconnector(vimconn.vimconnector):
             self._reload_connection()
             self.nova.images.delete(image_id)
             return image_id
-        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError) as e: #TODO remove
+        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e: #TODO remove
             self._format_exception(e)
 
     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''' 
         try:
             self._reload_connection()
             images = self.nova.images.list()
@@ -541,9 +575,38 @@ class vimconnector(vimconn.vimconnector):
                 if image.metadata.get("location")==path:
                     return image.id
             raise vimconn.vimconnNotFoundException("image with location '{}' not found".format( path))
-        except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError) as e: 
+        except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
             self._format_exception(e)
         
+    def get_image_list(self, filter_dict={}):
+        '''Obtain tenant images from VIM
+        Filter_dict can be:
+            id: image id
+            name: image name
+            checksum: image checksum
+        Returns the image list of dictionaries:
+            [{<the fields at Filter_dict plus some VIM specific>}, ...]
+            List can be empty
+        '''
+        self.logger.debug("Getting image list from VIM filter: '%s'", str(filter_dict))
+        try:
+            self._reload_connection()
+            filter_dict_os=filter_dict.copy()
+            #First we filter by the available filter fields: name, id. The others are removed.
+            filter_dict_os.pop('checksum',None)
+            image_list=self.nova.images.findall(**filter_dict_os)
+            if len(image_list)==0:
+                return []
+            #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 image_dict['checksum']==filter_dict.get('checksum'):
+                    filtered_list.append(image)
+            return filtered_list
+        except (ksExceptions.ClientException, nvExceptions.ClientException, gl1Exceptions.CommunicationError, ConnectionError) as e:
+            self._format_exception(e)
+
     def new_vminstance(self,name,description,start,image_id,flavor_id,net_list,cloud_config=None):
         '''Adds a VM instance to VIM
         Params:
@@ -660,8 +723,8 @@ class vimconnector(vimconn.vimconnector):
 #        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,
-                neClient.exceptions.ConnectionFailed) as e:
+        except (ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError
+                ) as e:
             self._format_exception(e)
         except TypeError as e:
             raise vimconn.vimconnException(type(e).__name__ + ": "+  str(e), http_code=vimconn.HTTP_Bad_Request)
@@ -674,7 +737,7 @@ class vimconnector(vimconn.vimconnector):
             server = self.nova.servers.find(id=vm_id)
             #TODO parse input and translate to VIM format (openmano_schemas.new_vminstance_response_schema)
             return server.to_dict()
-        except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound) as e:
+        except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
             self._format_exception(e)
 
     def get_vminstance_console(self,vm_id, console_type="vnc"):
@@ -725,7 +788,7 @@ class vimconnector(vimconn.vimconnector):
                     return console_dict
             raise vimconn.vimconnUnexpectedResponse("Unexpected response from VIM")
             
-        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest) as e:
+        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.BadRequest, ConnectionError) as e:
             self._format_exception(e)
 
     def delete_vminstance(self, vm_id):
@@ -743,7 +806,7 @@ class vimconnector(vimconn.vimconnector):
                     self.logger.error("Error deleting port: " + type(e).__name__ + ": "+  str(e))
             self.nova.servers.delete(vm_id)
             return vm_id
-        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException) as e:
+        except (nvExceptions.NotFound, ksExceptions.ClientException, nvExceptions.ClientException, ConnectionError) as e:
             self._format_exception(e)
         #TODO insert exception vimconn.HTTP_Unauthorized
         #if reaching here is because an exception
@@ -782,7 +845,10 @@ class vimconnector(vimconn.vimconnector):
                 else:
                     vm['status']    = "OTHER"
                     vm['error_msg'] = "VIM status reported " + vm_vim['status']
-                vm['vim_info']  = yaml.safe_dump(vm_vim)
+                try:
+                    vm['vim_info']  = yaml.safe_dump(vm_vim, default_flow_style=True, width=256)
+                except yaml.representer.RepresenterError:
+                    vm['vim_info'] = str(vm_vim)
                 vm["interfaces"] = []
                 if vm_vim.get('fault'):
                     vm['error_msg'] = str(vm_vim['fault'])
@@ -792,7 +858,10 @@ class vimconnector(vimconn.vimconnector):
                     port_dict=self.neutron.list_ports(device_id=vm_id)
                     for port in port_dict["ports"]:
                         interface={}
-                        interface['vim_info']  = yaml.safe_dump(port)
+                        try:
+                            interface['vim_info'] = yaml.safe_dump(port, default_flow_style=True, width=256)
+                        except yaml.representer.RepresenterError:
+                            interface['vim_info'] = str(port)
                         interface["mac_address"] = port.get("mac_address")
                         interface["vim_net_id"] = port["network_id"]
                         interface["vim_interface_id"] = port["id"]
@@ -889,7 +958,7 @@ class vimconnector(vimconn.vimconnector):
                     raise vimconn.vimconnException("Unexpected response from VIM " + str(console_dict))
             
             return vm_id
-        except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound) as e:
+        except (ksExceptions.ClientException, nvExceptions.ClientException, nvExceptions.NotFound, ConnectionError) as e:
             self._format_exception(e)
         #TODO insert exception vimconn.HTTP_Unauthorized