update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[osm/SO.git] / rwcal / plugins / vala / rwcal_openstack / rwcal_openstack.py
index eac0d6c..66859c7 100644 (file)
 #   limitations under the License.
 #
 
-import contextlib
 import logging
 import os
 import subprocess
 import tempfile
 import yaml
+import shlex
 
 import gi
 gi.require_version('RwCal', '1.0')
@@ -32,8 +32,6 @@ import rift.rwcal.openstack as openstack_drv
 import rw_status
 import rift.cal.rwcal_status as rwcal_status
 import rwlogger
-import neutronclient.common.exceptions as NeutronException
-import keystoneclient.exceptions as KeystoneExceptions
 
 
 from gi.repository import (
@@ -44,9 +42,9 @@ from gi.repository import (
 
 PREPARE_VM_CMD = "prepare_vm.py --auth_url {auth_url} --username {username} --password {password} --tenant_name {tenant_name} --region {region} --user_domain {user_domain} --project_domain {project_domain} --mgmt_network {mgmt_network} --server_id {server_id} --port_metadata "
 
-rwstatus_exception_map = { IndexError: RwTypes.RwStatus.NOTFOUND,
-                           KeyError: RwTypes.RwStatus.NOTFOUND,
-                           NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED,}
+rwstatus_exception_map = {IndexError: RwTypes.RwStatus.NOTFOUND,
+                          KeyError: RwTypes.RwStatus.NOTFOUND,
+                          NotImplementedError: RwTypes.RwStatus.NOT_IMPLEMENTED, }
 
 rwstatus = rw_status.rwstatus_from_exc_map(rwstatus_exception_map)
 rwcalstatus = rwcal_status.rwcalstatus_from_exc_map(rwstatus_exception_map)
@@ -66,6 +64,8 @@ class OpenstackServerGroupError(Exception):
 class ImageUploadError(Exception):
     pass
 
+class PrepareVduOnBoot(Exception):
+    pass
 
 class RwcalAccountDriver(object):
     """
@@ -75,9 +75,6 @@ class RwcalAccountDriver(object):
         self.log = logger
         try:
             self._driver = openstack_drv.OpenstackDriver(logger = self.log, **kwargs)
-        except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure,
-                NeutronException.NotFound) as e:
-            raise
         except Exception as e:
             self.log.error("RwcalOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e)))
             raise
@@ -153,35 +150,12 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             Validation Code and Details String
         """
-        status = RwcalYang.CloudConnectionStatus()
-        drv = self._use_driver(account) 
+        status = RwcalYang.YangData_Rwcal_ConnectionStatus()
         try:
+            drv = self._use_driver(account) 
             drv.validate_account_creds()
-        except KeystoneExceptions.Unauthorized as e:
-            self.log.error("Invalid credentials given for VIM account %s", account.name)
-            status.status = "failure"
-            status.details = "Invalid Credentials: %s" % str(e)
-
-        except KeystoneExceptions.AuthorizationFailure as e:
-            self.log.error("Bad authentication URL given for VIM account %s. Given auth url: %s",
-                           account.name, account.openstack.auth_url)
-            status.status = "failure"
-            status.details = "Invalid auth url: %s" % str(e)
-
-        except NeutronException.NotFound as e:
-            self.log.error("Given management network %s could not be found for VIM account %s",
-                           account.openstack.mgmt_network,
-                           account.name)
-            status.status = "failure"
-            status.details = "mgmt network does not exist: %s" % str(e)
-
-        except openstack_drv.ValidationError as e:
-            self.log.error("RwcalOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e))
-            status.status = "failure"
-            status.details = "Invalid Credentials: %s" % str(e)
-
         except Exception as e:
-            msg = "RwcalOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
+            msg = "RwcalOpenstackPlugin: Exception: %s" %(str(e))
             self.log.error(msg)
             status.status = "failure"
             status.details = msg
@@ -347,7 +321,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             The the list of images in VimResources object
         """
-        response = RwcalYang.VimResources()
+        response = RwcalYang.YangData_RwProject_Project_VimResources()
         drv = self._use_driver(account)
         try:
             images = drv.glance_image_list()
@@ -400,11 +374,8 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             kwargs['image_id']  = vminfo.image_id
 
         ### If floating_ip is required and we don't have one, better fail before any further allocation
-        pool_name = None
         floating_ip = False
         if vminfo.has_field('allocate_public_address') and vminfo.allocate_public_address:
-            if account.openstack.has_field('floating_ip_pool'):
-                pool_name = account.openstack.floating_ip_pool
             floating_ip = True
 
         if vminfo.has_field('cloud_init') and vminfo.cloud_init.has_field('userdata'):
@@ -441,7 +412,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             kwargs['availability_zone'] = None
 
         if vminfo.has_field('server_group'):
-            kwargs['scheduler_hints'] = {'group': vminfo.server_group }
+            kwargs['scheduler_hints'] = {'group': vminfo.server_group}
         else:
             kwargs['scheduler_hints'] = None
 
@@ -510,7 +481,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             Protobuf Gi object for VM
         """
-        vm = RwcalYang.VMInfoItem()
+        vm = RwcalYang.YangData_RwProject_Project_VimResources_VminfoList()
         vm.vm_id     = vm_info['id']
         vm.vm_name   = vm_info['name']
         vm.image_id  = vm_info['image']['id']
@@ -540,10 +511,10 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             if key in vm.user_tags.fields:
                 setattr(vm.user_tags, key, value)
         if 'OS-EXT-SRV-ATTR:host' in vm_info:
-            if vm_info['OS-EXT-SRV-ATTR:host'] != None:
+            if vm_info['OS-EXT-SRV-ATTR:host'] is not None:
                 vm.host_name = vm_info['OS-EXT-SRV-ATTR:host']
         if 'OS-EXT-AZ:availability_zone' in vm_info:
-            if vm_info['OS-EXT-AZ:availability_zone'] != None:
+            if vm_info['OS-EXT-AZ:availability_zone'] is not None:
                 vm.availability_zone = vm_info['OS-EXT-AZ:availability_zone']
         return vm
 
@@ -557,7 +528,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             List containing VM information
         """
-        response = RwcalYang.VimResources()
+        response = RwcalYang.YangData_RwProject_Project_VimResources()
         drv = self._use_driver(account)
         vms = drv.nova_server_list()
         for vm in vms:
@@ -630,7 +601,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             List of flavors
         """
-        response = RwcalYang.VimResources()
+        response = RwcalYang.YangData_RwProject_Project_VimResources()
         drv = self._use_driver(account)
         try:
             flavors = drv.nova_flavor_list()
@@ -677,10 +648,10 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             Network info item
         """
-        network                  = RwcalYang.NetworkInfoItem()
+        network                  = RwcalYang.YangData_RwProject_Project_VimResources_NetworkinfoList()
         network.network_name     = network_info['name']
         network.network_id       = network_info['id']
-        if ('provider:network_type' in network_info) and (network_info['provider:network_type'] != None):
+        if ('provider:network_type' in network_info) and (network_info['provider:network_type'] is not None):
             network.provider_network.overlay_type = network_info['provider:network_type'].upper()
         if ('provider:segmentation_id' in network_info) and (network_info['provider:segmentation_id']):
             network.provider_network.segmentation_id = network_info['provider:segmentation_id']
@@ -704,7 +675,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             List of networks
         """
-        response = RwcalYang.VimResources()
+        response = RwcalYang.YangData_RwProject_Project_VimResources()
         drv = self._use_driver(account)
         networks = drv.neutron_network_list()
         for network in networks:
@@ -754,10 +725,15 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             if network.provider_network.has_field('segmentation_id'):
                 kwargs['segmentation_id'] = network.provider_network.segmentation_id
 
-        drv = self._use_driver(account)
-        network_id = drv.neutron_network_create(**kwargs)
-        drv.neutron_subnet_create(network_id = network_id,
-                                  cidr = network.subnet)
+        try:
+            drv = self._use_driver(account)
+            network_id = drv.neutron_network_create(**kwargs)
+            drv.neutron_subnet_create(network_id = network_id,
+                                    cidr = network.subnet)
+        except Exception as e:
+            self.log.exception("Exception %s occured during create-network", str(e))
+            raise
+
         return network_id
 
     @rwstatus
@@ -784,7 +760,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             Port info item
         """
-        port = RwcalYang.PortInfoItem()
+        port = RwcalYang.YangData_RwProject_Project_VimResources_PortinfoList()
 
         port.port_name  = port_info['name']
         port.port_id    = port_info['id']
@@ -821,7 +797,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             Port info list
         """
-        response = RwcalYang.VimResources()
+        response = RwcalYang.YangData_RwProject_Project_VimResources()
         drv = self._use_driver(account)
         ports = drv.neutron_port_list(*{})
         for port in ports:
@@ -927,17 +903,25 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         Returns:
             A kwargs dictionary for glance operation
         """
-        
         drv = self._use_driver(account)
+        network_id = None
         try:
             kwargs = drv.utils.network.make_virtual_link_args(link_params)
             network_id = drv.neutron_network_create(**kwargs)
-            kwargs = drv.utils.network.make_subnet_args(link_params, network_id)
-            drv.neutron_subnet_create(**kwargs)
+            drv.utils.network.prepare_virtual_link(link_params, network_id)
         except Exception as e:
-            self.log.error("Encountered exceptions during network creation. Exception: %s", str(e))
-            raise
-
+            self.log.exception("Encountered exceptions during network creation. Exception: %s", str(e))
+            # This is to delete the network if neutron_subnet_create fails after creation of network
+            # Note:- Any subnet created will be implicitly deleted.
+            if network_id is not None:
+                try:
+                    drv.neutron_network_delete(network_id)
+                except Exception as delete_exception:
+                    self.log.debug("Exception while deleting the network after failure of neutron_subnet_create or make_subnet_args: %s", str(delete_exception))
+                    # Raising exception so that the Exception is propagated to Resmgr.
+                    # This fixes the UI Stuck at Vl-Init-Phase.
+                    raise Exception(str(e) + " --> " + str(delete_exception))
+            raise e
         return network_id
 
 
@@ -972,7 +956,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             link_id  - id for the virtual-link
 
         Returns:
-            Object of type RwcalYang.VirtualLinkInfoParams
+            Object of type RwcalYang.YangData_RwProject_Project_VnfResources_VirtualLinkInfoList
         """
         drv = self._use_driver(account)
         try:
@@ -989,6 +973,34 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             raise
         return virtual_link
 
+    @rwstatus(ret_on_failure=[None])
+    def do_get_virtual_link_by_name(self, account, link_name):
+        """Get information about virtual link.
+
+        Arguments:
+            account  - a cloud account
+            link_name  - name for the virtual-link
+
+        Returns:
+            Object of type RwcalYang.YangData_RwProject_Project_VnfResources_VirtualLinkInfoList
+        """
+        drv = self._use_driver(account)
+        try:
+            network = drv.neutron_network_get_by_name(link_name)
+            if network:
+                port_list = drv.neutron_port_list(**{'network_id': network['id']})
+                if 'subnets' in network and network['subnets']:
+                    subnet = drv.neutron_subnet_get(network['subnets'][0])
+                else:
+                    subnet = None
+                virtual_link = drv.utils.network.parse_cloud_virtual_link_info(network, port_list, subnet)
+            else:
+                return None
+        except Exception as e:
+            self.log.exception("Exception %s occured during virtual-link-get-by-name", str(e))
+            raise
+        return virtual_link
+
     @rwstatus(ret_on_failure=[None])
     def do_get_virtual_link_list(self, account):
         """Get information about all the virtual links
@@ -997,9 +1009,9 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             account  - a cloud account
 
         Returns:
-            A list of objects of type RwcalYang.VirtualLinkInfoParams
+            A list of objects of type RwcalYang.YangData_RwProject_Project_VnfResources_VirtualLinkInfoList
         """
-        vnf_resources = RwcalYang.VNFResources()
+        vnf_resources = RwcalYang.YangData_RwProject_Project_VnfResources()
         drv =  self._use_driver(account)
         try:
             networks = drv.neutron_network_list()
@@ -1024,37 +1036,53 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
 
         Arguments:
             account     - a cloud account
-            vdu_init  - information about VDU to create (RwcalYang.VDUInitParams)
+            vdu_init  - information about VDU to create (RwcalYang.YangData_RwProject_Project_VduInitParams)
 
         Returns:
             The vdu_id
         """
         drv =  self._use_driver(account)
+        vdu_prepared = True
         try:
             kwargs = drv.utils.compute.make_vdu_create_args(vdu_init, account)
             vm_id = drv.nova_server_create(**kwargs)
-            self.prepare_vdu_on_boot(account, vm_id, vdu_init)
+            vdu_prepared_on_boot = self.prepare_vdu_on_boot(account, vm_id, vdu_init)
+            vdu_prepared = vdu_prepared_on_boot["status"]
         except Exception as e:
             self.log.exception("Exception %s occured during create-vdu", str(e))
             raise
+
+        if vdu_prepared is False:
+            drv.utils.compute.perform_vdu_network_cleanup(vm_id)
+            drv.nova_server_delete(vm_id)
+            self.log.exception("Cleaning Up VDU as Prepare Vdu Failed : %s", vdu_prepared_on_boot["exception"])
+            raise PrepareVduOnBoot(vdu_prepared_on_boot["exception"])
         return vm_id
     
 
     def prepare_vdu_on_boot(self, account, server_id, vdu_params):
-        cmd = PREPARE_VM_CMD.format(auth_url       = account.openstack.auth_url,
-                                    username       = account.openstack.key,
-                                    password       = account.openstack.secret,
-                                    tenant_name    = account.openstack.tenant,
-                                    region         = account.openstack.region,
-                                    user_domain    = account.openstack.user_domain,
-                                    project_domain = account.openstack.project_domain,
-                                    mgmt_network   = account.openstack.mgmt_network,
-                                    server_id      = server_id)
+        if vdu_params.mgmt_network is not None:
+            mgmt_network_param = vdu_params.mgmt_network
+        else:
+            mgmt_network_param = account.openstack.mgmt_network
+
+        # Adding shell quote to all parameters in case they contain special characters.
+        cmd = PREPARE_VM_CMD.format(auth_url       = shlex.quote(account.openstack.auth_url),
+                                    username       = shlex.quote(account.openstack.key),
+                                    password       = shlex.quote(account.openstack.secret),
+                                    tenant_name    = shlex.quote(account.openstack.tenant),
+                                    region         = shlex.quote(account.openstack.region),
+                                    user_domain    = shlex.quote(account.openstack.user_domain),
+                                    project_domain = shlex.quote(account.openstack.project_domain),
+                                    mgmt_network   = shlex.quote(mgmt_network_param),
+                                    server_id      = shlex.quote(server_id))
         vol_list = list()
+
+        vdu_prepared = {"status": True, "exception": None}
         
         if vdu_params.has_field('allocate_public_address') and vdu_params.allocate_public_address:
-            cmd += " --floating_ip"
             if account.openstack.has_field('floating_ip_pool'):
+                cmd += " --floating_ip"
                 cmd += (" --pool_name " + account.openstack.floating_ip_pool)
         
         if vdu_params.has_field('volumes'):
@@ -1066,19 +1094,34 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp_file:
                 yaml.dump(vol_list, tmp_file)
                 cmd += (" --vol_metadata {}").format(tmp_file.name)
-            
+        
+        
         exec_path = 'python3 ' + os.path.dirname(openstack_drv.__file__)
-        exec_cmd = exec_path+'/'+cmd
+        exec_cmd = exec_path + '/' + cmd
         self.log.info("Running command: %s" %(exec_cmd))
-        subprocess.call(exec_cmd, shell=True)
-
+        
+        # The return code for the subprocess is always 0. Hence using check_output
+        # for validating the success/failure of the script
+        # The check_output returns False or True on the basis of exception
+        # handling in prepare_vm.py
+        prepare_vm_status = subprocess.check_output(exec_cmd, shell=True)
+        
+        # prepare_vm_status is a string in the format of True/False+ error message
+        # if any. This is to propagate the detailed exception to the callers.
+        vdu_status_elems = prepare_vm_status.decode("utf-8").split("+")
+        if(vdu_status_elems[0] == 'False'):
+            self.log.exception("Exception occured while preparing vdu after boot")
+            vdu_prepared = {"status": False, "exception": vdu_status_elems[1]}
+
+        return vdu_prepared                      
+        
     @rwstatus
     def do_modify_vdu(self, account, vdu_modify):
         """Modify Properties of existing virtual deployment unit
 
         Arguments:
             account     -  a cloud account
-            vdu_modify  -  Information about VDU Modification (RwcalYang.VDUModifyParams)
+            vdu_modify  -  Information about VDU Modification (RwcalYang.YangData_RwProject_Project_VduModifyParams)
         """
         drv = self._use_driver(account)
         ### First create required number of ports aka connection points
@@ -1125,29 +1168,33 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             raise
             
 
-    @rwstatus(ret_on_failure=[None])
-    def do_get_vdu(self, account, vdu_id):
+    @rwcalstatus(ret_on_failure=[None])
+    def do_get_vdu(self, account, vdu_id, mgmt_network):
         """Get information about a virtual deployment unit.
 
         Arguments:
             account - a cloud account
             vdu_id  - id for the vdu
+            mgmt_network - mgmt_network if provided in NSD VL
 
         Returns:
-            Object of type RwcalYang.VDUInfoParams
+            Object of type RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList
         """
+        if mgmt_network not in [None, ""]:
+            account.openstack.mgmt_network = mgmt_network
+
         drv = self._use_driver(account)
         try:
             vm_info = drv.nova_server_get(vdu_id)
             vdu_info = drv.utils.compute.parse_cloud_vdu_info(vm_info)
         except Exception as e:
-            self.log.exception("Exception %s occured during get-vdu", str(e))
-            raise
+            self.log.debug("Exception occured during get-vdu: %s", str(e))
+            raise 
         
         return vdu_info
 
 
-    @rwstatus(ret_on_failure=[None])
+    @rwcalstatus(ret_on_failure=[None])
     def do_get_vdu_list(self, account):
         """Get information about all the virtual deployment units
 
@@ -1155,9 +1202,9 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             account     - a cloud account
 
         Returns:
-            A list of objects of type RwcalYang.VDUInfoParams
+            A list of objects of type RwcalYang.YangData_RwProject_Project_VnfResources_VduInfoList
         """
-        vnf_resources = RwcalYang.VNFResources()
+        vnf_resources = RwcalYang.YangData_RwProject_Project_VnfResources()
         drv = self._use_driver(account)
         try:
             vms = drv.nova_server_list()
@@ -1165,8 +1212,8 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
                 vdu = drv.utils.compute.parse_cloud_vdu_info(vm)
                 vnf_resources.vdu_info_list.append(vdu)
         except Exception as e:
-            self.log.exception("Exception %s occured during get-vdu-list", str(e))
-            raise
+            self.log.debug("Exception occured during get-vdu-list: %s", str(e))
+            raise 
         return vnf_resources