Merge "Commit to master [RIFT 15737] Remove logic for deletion of folder on deletion...
authorvelandy <rajesh.velandy@riftio.com>
Mon, 13 Mar 2017 15:35:35 +0000 (16:35 +0100)
committerGerrit Code Review <root@osm.etsi.org>
Mon, 13 Mar 2017 15:35:35 +0000 (16:35 +0100)
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/portchain/portchain_drv.py
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py
rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/network.py
rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py
rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt
rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/CMakeLists.txt [new file with mode: 0644]
rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py [new file with mode: 0644]
rwlaunchpad/plugins/rwvns/yang/rwsdn.yang

index 500f10c..ffab014 100644 (file)
@@ -25,7 +25,6 @@ from . import neutron as nt_drv
 from . import glance as gl_drv
 from . import ceilometer as ce_drv
 from . import cinder as ci_drv
-from . import portchain as port_drv
 from . import utils as drv_utils
 
 # Exceptions
@@ -142,9 +141,6 @@ class OpenstackDriver(object):
                                                  region_name = region,
                                                  logger = self.log)
         
-        self.portchain_drv = port_drv.L2PortChainDriver(self.sess_drv,
-                                                        self.neutron_drv,
-                                                        logger = self.log)
         self.utils = DriverUtilities(self)
         
         self._mgmt_network = mgmt_network
@@ -575,83 +571,6 @@ class OpenstackDriver(object):
     def ceilo_alarm_delete(self, alarm_id):
         self.ceilo_drv.client.alarms.delete(alarm_id)
 
-    def create_port_chain(self,name,port_lists):
-        "Create port chain"
-        #Create port pair
-        ppgrp_list = list()
-        for index,port_pair in  enumerate(port_lists):
-            ppair_list = list()
-            ingress_port,egress_port = port_pair
-            #Disable security group and port security for the port
-            self.neutron_drv.port_update(ingress_port,no_security_groups=True,port_security_enabled=False)
-            if ingress_port != egress_port:
-                self.neutron_drv.port_update(egress_port,no_security_groups=True,port_security_enabled=False)
-
-            ppair_id = self.portchain_drv.create_port_pair(name+'ppair'+str(index),ingress_port,egress_port)
-            ppair_list.append(ppair_id)
-            # Create port pair group
-            ppgrp_id = self.portchain_drv.create_port_pair_group(name+'_ppgrp_'+str(index),ppair_list)
-            ppgrp_list.append(ppgrp_id)
-        #Create port chain
-        port_chain_id = self.portchain_drv.create_port_chain(name,ppgrp_list)
-        return port_chain_id
-
-    def delete_port_chain(self,port_chain_id):
-        "Delete port chain"
-        try:
-            result = self.portchain_drv.get_port_chain(port_chain_id)
-            port_chain = result.json() 
-            self.log.debug("Port chain result is %s", port_chain)
-            port_pair_groups = port_chain["port_chain"]["port_pair_groups"]
-            self.portchain_drv.delete_port_chain(port_chain_id)
-
-            # Get port pairs and delete port pair groups
-            port_pairs = list()
-            self.log.debug("Port pair groups during delete is %s", port_pair_groups)
-            for port_pair_group_id in port_pair_groups:
-                result = self.portchain_drv.get_port_pair_group(port_pair_group_id)
-                port_pair_group = result.json() 
-                self.log.debug("Port pair group result is %s", port_pair_group)
-                port_pairs.extend(port_pair_group["port_pair_group"]["port_pairs"])
-                self.portchain_drv.delete_port_pair_group(port_pair_group_id)
-
-            self.log.debug("Port pairs during delete is %s",port_pairs)
-
-            for port_pair_id in port_pairs:
-                self.portchain_drv.delete_port_pair(port_pair_id)
-                pass
-        except Exception as e:
-            self.log.error("Error while delete port chain with id %s, exception %s", port_chain_id,str(e))
-           
-    def update_port_chain(self,port_chain_id,flow_classifier_list):
-        result = self.portchain_drv.get_port_chain(port_chain_id)
-        result.raise_for_status()
-        port_chain = result.json()['port_chain']
-        new_flow_classifier_list = list()
-        if port_chain and port_chain['flow_classifiers']:
-           new_flow_classifier_list.extend(port_chain['flow_classifiers'])
-        new_flow_classifier_list.extend(flow_classifier_list)
-        port_chain_id = self.portchain_drv.update_port_chain(port_chain['id'],flow_classifiers=new_flow_classifier_list)
-        return port_chain_id
-        
-    def create_flow_classifer(self,classifier_name,classifier_dict):
-        "Create flow classifier"
-        flow_classifier_id = self.portchain_drv.create_flow_classifier(classifier_name,classifier_dict)
-        return flow_classifier_id
-
-    def delete_flow_classifier(self,classifier_id):
-        "Create flow classifier"
-        try:
-            self.portchain_drv.delete_flow_classifier(classifier_id)
-        except Exception as e:
-            self.log.error("Error while deleting flow classifier with id %s, exception %s", classifier_id,str(e))
-
-    def get_port_chain_list(self):
-        result = self.portchain_drv.get_port_chain_list()
-        port_chain_list = result.json()
-        if 'port_chains' in port_chain_list:
-            return port_chain_list['port_chains'] 
     def cinder_volume_list(self):
         return self.cinder_drv.volume_list()
   
index 9dd959d..78e805d 100644 (file)
@@ -30,12 +30,12 @@ class L2PortChainDriver(object):
     PORT_CHAINS_URL='/sfc/port_chains' 
     FLOW_CLASSIFIERS_URL='/sfc/flow_classifiers' 
 
-    def __init__(self, sess_handle, neutron_drv, logger = None):
+    def __init__(self, sess_handle, neutron_base_url, logger = None):
         """
         Constructor for L2PortChainDriver class
         Arguments: 
            sess_handle (instance of class SessionDriver)
-           neutron_drv 
+           neutron_base_url  Neutron service endpoint
            logger (instance of logging.Logger)
         """
         if logger is None:
@@ -45,8 +45,7 @@ class L2PortChainDriver(object):
             self.log = logger
 
         self._sess = sess_handle
-        self.neutron_drv = neutron_drv
-        self._neutron_base_url = neutron_drv.neutron_endpoint
+        self._neutron_base_url = neutron_base_url
         
     @property
     def neutron_base_url(self): 
index 55ece65..fc053c2 100644 (file)
@@ -192,24 +192,21 @@ class ComputeUtils(object):
                                       %(volume.name))
         
         kwargs['boot_index'] = volume.boot_priority
-        if "image" in volume:
+        if volume.has_field("image"):
             # Support image->volume
-            if volume.image is not None:
-                kwargs['source_type'] = "image"
-                kwargs['uuid'] = self.resolve_image_n_validate(volume.image, volume.image_checksum)
-            else:
-                # Support blank->volume
-                kwargs['source_type'] = "blank"
+            kwargs['source_type'] = "image"
+            kwargs['uuid'] = self.resolve_image_n_validate(volume.image, volume.image_checksum)
+        else:
+            # Support blank->volume
+            kwargs['source_type'] = "blank"
         kwargs['device_name'] = volume.name
         kwargs['destination_type'] = "volume"
         kwargs['volume_size'] = volume.size
         kwargs['delete_on_termination'] = True
 
         if volume.has_field('device_type'):
-            if volume.device_type == 'cdrom':
-                kwargs['device_type'] = 'cdrom'
-            elif volume.device_bus == 'ide':
-                kwargs['disk_bus'] = 'ide'
+            if volume.device_type in ['cdrom', 'disk']:
+                kwargs['device_type'] = volume.device_type
             else:
                 self.log.error("Unsupported device_type <%s> found for volume: %s",
                                volume.device_type, volume.name)
@@ -220,6 +217,21 @@ class ComputeUtils(object):
                            volume.name)
             raise VolumeValidateError("Mandatory field <device_type> not specified for volume: %s"
                                       %(volume.name))
+
+        if volume.has_field('device_bus'):
+            if volume.device_bus in ['ide', 'virtio', 'scsi']:
+                kwargs['disk_bus'] = volume.device_bus
+            else:
+                self.log.error("Unsupported device_bus <%s> found for volume: %s",
+                               volume.device_bus, volume.name)
+                raise VolumeValidateError("Unsupported device_bus <%s> found for volume: %s"
+                                          %(volume.device_bus, volume.name))        
+        else:
+            self.log.error("Mandatory field <device_bus> not specified for volume: %s",
+                           volume.name)
+            raise VolumeValidateError("Mandatory field <device_bus> not specified for volume: %s"
+                                      %(volume.name))
+
         return kwargs
             
     def make_vdu_storage_args(self, vdu_params):
@@ -235,6 +247,8 @@ class ComputeUtils(object):
         kwargs = dict()
         if vdu_params.has_field('volumes'):
             kwargs['block_device_mapping_v2'] = list()
+            # Ignore top-level image
+            kwargs['image_id']  = ""
             for volume in vdu_params.volumes:
                 kwargs['block_device_mapping_v2'].append(self.make_vdu_volume_args(volume, vdu_params))
         return kwargs
@@ -522,7 +536,6 @@ class ComputeUtils(object):
         console_url = None
         if self._parse_vdu_state_info(vm_info) == 'active':
             try:
-                console_url = self.driver.nova_server_console(vm_info['id'])
                 serv_console_url = self.driver.nova_server_console(vm_info['id'])
                 if 'console' in serv_console_url:
                     console_url = serv_console_url['console']['url']
index 8e5107b..8e6f608 100644 (file)
@@ -1,6 +1,6 @@
 #!/usr/bin/python
 
-#
+# 
 #   Copyright 2017 RIFT.IO Inc
 #
 #   Licensed under the Apache License, Version 2.0 (the "License");
@@ -131,7 +131,11 @@ class NetworkUtils(object):
             network_ids.append(self.driver._mgmt_network_id)
             
         ### Create ports and collect port ids
-        port_ids = self.driver.neutron_multi_port_create(port_args)
+        if port_args:
+            port_ids = self.driver.neutron_multi_port_create(port_args)
+        else:
+            port_ids = list()
+
         return port_ids, network_ids
     
         
index 52bd574..eac0d6c 100644 (file)
@@ -23,7 +23,6 @@ import tempfile
 import yaml
 
 import gi
-gi.require_version('RwSdn', '1.0')
 gi.require_version('RwCal', '1.0')
 gi.require_version('RwcalYang', '1.0')
 
@@ -40,8 +39,6 @@ import keystoneclient.exceptions as KeystoneExceptions
 from gi.repository import (
     GObject,
     RwCal,
-    RwSdn, # Vala package
-    RwsdnYang,
     RwTypes,
     RwcalYang)
 
@@ -96,18 +93,29 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
 
     def __init__(self):
         GObject.Object.__init__(self)
-        self._driver_class = openstack_drv.OpenstackDriver
         self.log = logging.getLogger('rwcal.openstack.%s' % RwcalOpenstackPlugin.instance_num)
         self.log.setLevel(logging.DEBUG)
         self._rwlog_handler = None
         self._account_drivers = dict()
         RwcalOpenstackPlugin.instance_num += 1
 
+    def _get_account_key(self, account):
+        key = str()
+        for f in account.openstack.fields:
+            try:
+                key+= str(getattr(account.openstack, f))
+            except:
+                pass
+        key += account.name
+        return key
+    
     def _use_driver(self, account):
         if self._rwlog_handler is None:
             raise UninitializedPluginError("Must call init() in CAL plugin before use.")
 
-        if account.name not in self._account_drivers:
+        acct_key = self._get_account_key(account)
+        
+        if acct_key not in self._account_drivers:
             self.log.debug("Creating OpenstackDriver")
             kwargs = dict(username = account.openstack.key,
                           password = account.openstack.secret,
@@ -122,7 +130,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             self._account_drivers[account.name] = drv
             return drv.driver
         else:
-            return self._account_drivers[account.name].driver
+            return self._account_drivers[acct_key].driver
         
 
     @rwstatus
@@ -584,12 +592,17 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             flavor id
         """
         drv = self._use_driver(account)
-        return drv.nova_flavor_create(name      = flavor.name,
-                                      ram       = flavor.vm_flavor.memory_mb,
-                                      vcpus     = flavor.vm_flavor.vcpu_count,
-                                      disk      = flavor.vm_flavor.storage_gb,
-                                      epa_specs = drv.utils.flavor.get_extra_specs(flavor))
-    
+        try:
+            flavor_id = drv.nova_flavor_create(name      = flavor.name,
+                                               ram       = flavor.vm_flavor.memory_mb,
+                                               vcpus     = flavor.vm_flavor.vcpu_count,
+                                               disk      = flavor.vm_flavor.storage_gb,
+                                               epa_specs = drv.utils.flavor.get_extra_specs(flavor))
+        except Exception as e:
+            self.log.error("Encountered exceptions during Flavor creation. Exception: %s", str(e))
+            raise
+            
+        return flavor_id  
 
     @rwstatus
     def do_delete_flavor(self, account, flavor_id):
@@ -600,7 +613,11 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             flavor_id - id flavor of the VM
         """
         drv = self._use_driver(account)
-        drv.nova_flavor_delete(flavor_id)
+        try:
+            drv.nova_flavor_delete(flavor_id)
+        except Exception as e:
+            self.log.error("Encountered exceptions during Flavor deletion. Exception: %s", str(e))
+            raise
 
 
     @rwstatus(ret_on_failure=[[]])
@@ -615,9 +632,14 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         """
         response = RwcalYang.VimResources()
         drv = self._use_driver(account)
-        flavors = drv.nova_flavor_list()
-        for flv in flavors:
-            response.flavorinfo_list.append(drv.utils.flavor.parse_flavor_info(flv))
+        try:
+            flavors = drv.nova_flavor_list()
+            for flv in flavors:
+                response.flavorinfo_list.append(drv.utils.flavor.parse_flavor_info(flv))
+        except Exception as e:
+            self.log.error("Encountered exceptions during get-flavor-list. Exception: %s", str(e))
+            raise
+
         return response
 
     @rwstatus(ret_on_failure=[None])
@@ -632,8 +654,14 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
             Flavor info item
         """
         drv = self._use_driver(account)
-        flavor = drv.nova_flavor_get(id)
-        return drv.utils.flavor.parse_flavor_info(flavor)
+        try:
+            flavor = drv.nova_flavor_get(id)
+            response = drv.utils.flavor.parse_flavor_info(flavor)
+        except Exception as e:
+            self.log.error("Encountered exceptions during get-flavor. Exception: %s", str(e))
+            raise
+        
+        return response
 
 
     def _fill_network_info(self, network_info, account):
@@ -904,12 +932,12 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         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)
         except Exception as e:
             self.log.error("Encountered exceptions during network creation. Exception: %s", str(e))
             raise
 
-        kwargs = drv.utils.network.make_subnet_args(link_params, network_id)
-        drv.neutron_subnet_create(**kwargs)
         return network_id
 
 
@@ -1142,176 +1170,3 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud):
         return vnf_resources
 
 
-class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
-    instance_num = 1
-    def __init__(self):
-        GObject.Object.__init__(self)
-        self._driver_class = openstack_drv.OpenstackDriver
-        self.log = logging.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin.instance_num)
-        self.log.setLevel(logging.DEBUG)
-
-        self._rwlog_handler = None
-        SdnOpenstackPlugin.instance_num += 1
-
-    @contextlib.contextmanager
-    def _use_driver(self, account):
-        if self._rwlog_handler is None:
-            raise UninitializedPluginError("Must call init() in CAL plugin before use.")
-
-        with rwlogger.rwlog_root_handler(self._rwlog_handler):
-            try:
-                drv = self._driver_class(username      = account.openstack.key,
-                                         password      = account.openstack.secret,
-                                         auth_url      = account.openstack.auth_url,
-                                         tenant_name   = account.openstack.tenant,
-                                         mgmt_network  = account.openstack.mgmt_network,
-                                         cert_validate = account.openstack.cert_validate )
-            except Exception as e:
-                self.log.error("SdnOpenstackPlugin: OpenstackDriver init failed. Exception: %s" %(str(e)))
-                raise
-
-            yield drv
-
-    @rwstatus
-    def do_init(self, rwlog_ctx):
-        self._rwlog_handler = rwlogger.RwLogger(
-                category="rw-cal-log",
-                subcategory="openstack",
-                log_hdl=rwlog_ctx,
-                )
-        self.log.addHandler(self._rwlog_handler)
-        self.log.propagate = False
-
-    @rwstatus(ret_on_failure=[None])
-    def do_validate_sdn_creds(self, account):
-        """
-        Validates the sdn account credentials for the specified account.
-        Performs an access to the resources using Keystone API. If creds
-        are not valid, returns an error code & reason string
-
-        @param account - a SDN account
-
-        Returns:
-            Validation Code and Details String
-        """
-        status = RwsdnYang.SdnConnectionStatus()
-        try:
-            with self._use_driver(account) as drv:
-                drv.validate_account_creds()
-
-        except openstack_drv.ValidationError as e:
-            self.log.error("SdnOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e))
-            status.status = "failure"
-            status.details = "Invalid Credentials: %s" % str(e)
-
-        except Exception as e:
-            msg = "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
-            self.log.error(msg)
-            status.status = "failure"
-            status.details = msg
-
-        else:
-            status.status = "success"
-            status.details = "Connection was successful"
-
-        return status
-
-    @rwstatus(ret_on_failure=[""])
-    def do_create_vnffg_chain(self, account,vnffg):
-        """
-        Creates Service Function chain in ODL
-
-        @param account - a SDN account
-
-        """
-        self.log.debug('Received Create VNFFG chain for account {}, chain {}'.format(account,vnffg))
-        with self._use_driver(account) as drv:
-            port_list = list()
-            vnf_chain_list = sorted(vnffg.vnf_chain_path, key = lambda x: x.order)
-            prev_vm_id = None 
-            for path in vnf_chain_list:
-                if prev_vm_id and path.vnfr_ids[0].vdu_list[0].vm_id == prev_vm_id:
-                    prev_entry = port_list.pop()
-                    port_list.append((prev_entry[0],path.vnfr_ids[0].vdu_list[0].port_id))
-                    prev_vm_id = None
-                else:
-                    prev_vm_id = path.vnfr_ids[0].vdu_list[0].vm_id
-                    port_list.append((path.vnfr_ids[0].vdu_list[0].port_id,path.vnfr_ids[0].vdu_list[0].port_id))
-            vnffg_id = drv.create_port_chain(vnffg.name,port_list)
-            return vnffg_id
-
-    @rwstatus
-    def do_terminate_vnffg_chain(self, account,vnffg_id):
-        """
-        Terminate Service Function chain in ODL
-
-        @param account - a SDN account
-        """
-        self.log.debug('Received terminate VNFFG chain for id %s ', vnffg_id)
-        with self._use_driver(account) as drv:
-            drv.delete_port_chain(vnffg_id)
-
-    @rwstatus(ret_on_failure=[None])
-    def do_create_vnffg_classifier(self, account, vnffg_classifier):
-        """
-           Add VNFFG Classifier 
-
-           @param account - a SDN account
-        """
-        self.log.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account,vnffg_classifier))
-        protocol_map = {1:'ICMP',6:'TCP',17:'UDP'}
-        flow_classifier_list = list()
-        with self._use_driver(account) as drv:
-            for rule in vnffg_classifier.match_attributes:
-                classifier_name = vnffg_classifier.name + '_' + rule.name
-                flow_dict = {} 
-                for field, value in rule.as_dict().items():
-                    if field == 'ip_proto':
-                        flow_dict['protocol'] = protocol_map.get(value,None)
-                    elif field == 'source_ip_address':
-                        flow_dict['source_ip_prefix'] = value
-                    elif field == 'destination_ip_address':
-                        flow_dict['destination_ip_prefix'] = value
-                    elif field == 'source_port':
-                        flow_dict['source_port_range_min'] = value
-                        flow_dict['source_port_range_max'] = value
-                    elif field == 'destination_port':
-                        flow_dict['destination_port_range_min'] = value
-                        flow_dict['destination_port_range_max'] = value
-                if vnffg_classifier.has_field('port_id'):
-                    flow_dict['logical_source_port'] = vnffg_classifier.port_id 
-                flow_classifier_id = drv.create_flow_classifer(classifier_name, flow_dict)
-                flow_classifier_list.append(flow_classifier_id)
-            drv.update_port_chain(vnffg_classifier.rsp_id,flow_classifier_list)
-        return flow_classifier_list
-
-    @rwstatus(ret_on_failure=[None])
-    def do_terminate_vnffg_classifier(self, account, vnffg_classifier_list):
-        """
-           Add VNFFG Classifier 
-
-           @param account - a SDN account
-        """
-        self.log.debug('Received terminate VNFFG classifier for id %s ', vnffg_classifier_list)
-        with self._use_driver(account) as drv:
-            for classifier_id in vnffg_classifier_list:
-                drv.delete_flow_classifier(classifier_id)
-
-    @rwstatus(ret_on_failure=[None])
-    def do_get_vnffg_rendered_paths(self, account):
-        """
-           Get ODL Rendered Service Path List (SFC)
-
-           @param account - a SDN account
-        """
-        self.log.debug('Received get VNFFG rendered path for account %s ', account)
-        vnffg_rsps = RwsdnYang.VNFFGRenderedPaths() 
-        with self._use_driver(account) as drv:
-            port_chain_list = drv.get_port_chain_list()
-            for port_chain in port_chain_list:
-                #rsp = vnffg_rsps.vnffg_rendered_path.add()
-                #rsp.name = port_chain['name']
-                pass
-        return vnffg_rsps
-
-
index 895ee85..4865d2e 100644 (file)
@@ -68,6 +68,7 @@ set(subdirs
   rwsdn_mock
   rwsdn_sim
   rwsdn_odl 
+  rwsdn_openstack
   rwsdn-python
   )
 rift_add_subdirs(SUBDIR_LIST ${subdirs})
diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/CMakeLists.txt b/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/CMakeLists.txt
new file mode 100644 (file)
index 0000000..fcf944f
--- /dev/null
@@ -0,0 +1,20 @@
+
+# 
+#   Copyright 2017 RIFT.IO Inc
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+
+include(rift_plugin)
+
+rift_install_python_plugin(rwsdn_openstack rwsdn_openstack.py)
diff --git a/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py b/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py
new file mode 100644 (file)
index 0000000..ff5d019
--- /dev/null
@@ -0,0 +1,378 @@
+
+#
+#   Copyright 2016 RIFT.IO Inc
+#
+#   Licensed under the Apache License, Version 2.0 (the "License");
+#   you may not use this file except in compliance with the License.
+#   You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#   Unless required by applicable law or agreed to in writing, software
+#   distributed under the License is distributed on an "AS IS" BASIS,
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#   See the License for the specific language governing permissions and
+#   limitations under the License.
+#
+
+import contextlib
+import logging
+
+import gi
+gi.require_version('RwSdn', '1.0')
+gi.require_version('RwCal', '1.0')
+gi.require_version('RwcalYang', '1.0')
+
+from rift.rwcal.openstack import session as sess_drv
+from rift.rwcal.openstack import keystone as ks_drv
+from rift.rwcal.openstack import neutron as nt_drv
+from rift.rwcal.openstack import portchain as port_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 (
+    GObject,
+    RwCal,
+    RwSdn, # Vala package
+    RwsdnYang,
+    RwTypes,
+    RwcalYang)
+
+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)
+
+
+class OpenstackSdnOperationFailure(Exception):
+    pass
+
+class UninitializedPluginError(Exception):
+    pass
+
+class OpenstackL2PortChainingDriver(object):
+    """
+    Driver for openstack keystone and neutron
+    """
+    def __init__(self, logger = None, **kwargs):
+        """
+        OpenstackDriver Driver constructor
+        Arguments:
+           logger: (instance of logging.Logger)
+           kwargs:  A dictionary of 
+            {
+              username (string)                   : Username for project/tenant.
+              password (string)                   : Password
+              auth_url (string)                   : Keystone Authentication URL.
+              project  (string)                   : Openstack project name
+              cert_validate (boolean, optional)   : In case of SSL/TLS connection if certificate validation is required or not.
+              user_domain                         : Domain name for user
+              project_domain                      : Domain name for project
+              region                              : Region name
+            }
+        """
+
+        if logger is None:
+            self.log = logging.getLogger('rwsdn.openstack.driver')
+            self.log.setLevel(logging.DEBUG)
+        else:
+            self.log = logger
+
+        args =  dict(auth_url            = kwargs['auth_url'],
+                     username            = kwargs['username'],
+                     password            = kwargs['password'],
+                     project_name        = kwargs['project'],
+                     project_domain_name = kwargs['project_domain'] if 'project_domain' in kwargs else None,
+                     user_domain_name    = kwargs['user_domain'] if 'user_domain' in kwargs else None,)
+
+        cert_validate = kwargs['cert_validate'] if 'cert_validate' in kwargs else False
+        region = kwargs['region_name'] if 'region_name' in kwargs else False
+
+        discover = ks_drv.KeystoneVersionDiscover(kwargs['auth_url'], logger = self.log)
+        (major, minor) = discover.get_version()
+
+        self.sess_drv = sess_drv.SessionDriver(auth_method = 'password',
+                                               version = str(major),
+                                               cert_validate = cert_validate,
+                                               logger = self.log,
+                                               **args)
+
+        self.neutron_drv = nt_drv.NeutronDriver(self.sess_drv,
+                                                region_name = region,
+                                                logger = self.log)
+
+        self.portchain_drv = port_drv.L2PortChainDriver(self.sess_drv,
+                                                        self.neutron_drv.neutron_endpoint,
+                                                        logger = self.log)
+
+    def validate_account_creds(self):
+        try:
+            self.sess_drv.invalidate_auth_token()
+            self.sess_drv.auth_token
+        except KeystoneExceptions.AuthorizationFailure as e:
+            self.log.error("Unable to authenticate or validate the existing credentials. Exception: %s", str(e))
+            raise ValidationError("Invalid Credentials: "+ str(e))
+        except Exception as e:
+            self.log.error("Could not connect to Openstack. Exception: %s", str(e))
+            raise ValidationError("Connection Error: "+ str(e))
+
+    def delete_port_chain(self,port_chain_id):
+        "Delete port chain"
+        try:
+            result = self.portchain_drv.get_port_chain(port_chain_id)
+            port_chain = result.json()
+            self.log.debug("Port chain result is %s", port_chain)
+            port_pair_groups = port_chain["port_chain"]["port_pair_groups"]
+            self.portchain_drv.delete_port_chain(port_chain_id)
+
+            # Get port pairs and delete port pair groups
+            port_pairs = list()
+            self.log.debug("Port pair groups during delete is %s", port_pair_groups)
+            for port_pair_group_id in port_pair_groups:
+                result = self.portchain_drv.get_port_pair_group(port_pair_group_id)
+                port_pair_group = result.json()
+                self.log.debug("Port pair group result is %s", port_pair_group)
+                port_pairs.extend(port_pair_group["port_pair_group"]["port_pairs"])
+                self.portchain_drv.delete_port_pair_group(port_pair_group_id)
+
+            self.log.debug("Port pairs during delete is %s",port_pairs)
+
+            for port_pair_id in port_pairs:
+                self.portchain_drv.delete_port_pair(port_pair_id)
+                pass
+        except Exception as e:
+            self.log.error("Error while delete port chain with id %s, exception %s", port_chain_id,str(e))
+
+    def update_port_chain(self,port_chain_id,flow_classifier_list):
+        result = self.portchain_drv.get_port_chain(port_chain_id)
+        result.raise_for_status()
+        port_chain = result.json()['port_chain']
+        new_flow_classifier_list = list()
+        if port_chain and port_chain['flow_classifiers']:
+           new_flow_classifier_list.extend(port_chain['flow_classifiers'])
+        new_flow_classifier_list.extend(flow_classifier_list)
+        port_chain_id = self.portchain_drv.update_port_chain(port_chain['id'],flow_classifiers=new_flow_classifier_list)
+        return port_chain_id
+
+    def create_flow_classifer(self,classifier_name,classifier_dict):
+        "Create flow classifier"
+        flow_classifier_id = self.portchain_drv.create_flow_classifier(classifier_name,classifier_dict)
+        return flow_classifier_id
+
+    def delete_flow_classifier(self,classifier_id):
+        "Create flow classifier"
+        try:
+            self.portchain_drv.delete_flow_classifier(classifier_id)
+        except Exception as e:
+            self.log.error("Error while deleting flow classifier with id %s, exception %s", classifier_id,str(e))
+
+    def get_port_chain_list(self):
+        result = self.portchain_drv.get_port_chain_list()
+        port_chain_list = result.json()
+        if 'port_chains' in port_chain_list:
+            return port_chain_list['port_chains']
+
+
+class RwsdnAccountDriver(object):                                                                             
+      """
+      Container class per sdn account                                                                         
+      """ 
+      def __init__(self, logger, **kwargs):                                                                     
+          self.log = logger
+          try:
+              self._driver = OpenstackL2PortChainingDriver(logger = self.log, **kwargs)                         
+          except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure,                     
+                  NeutronException.NotFound) as e:                                                              
+              raise
+          except Exception as e:
+              self.log.error("RwsdnOpenstackPlugin: OpenstackL2PortChainingDriver init failed. Exception: %s" %(str(e)))      
+              raise
+  
+      @property                                                                                                 
+      def driver(self):
+          return self._driver
+
+    
+class SdnOpenstackPlugin(GObject.Object, RwSdn.Topology):
+    instance_num = 1
+    def __init__(self):
+        GObject.Object.__init__(self)
+        self.log = logging.getLogger('rwsdn.openstack.%s' % SdnOpenstackPlugin.instance_num)
+        self.log.setLevel(logging.DEBUG)
+
+        self._rwlog_handler = None
+        self._account_drivers = dict()
+        SdnOpenstackPlugin.instance_num += 1
+
+    def _use_driver(self, account):
+        if self._rwlog_handler is None:
+            raise UninitializedPluginError("Must call init() in SDN plugin before use.")
+
+        if account.name not in self._account_drivers:
+            self.log.debug("Creating SDN OpenstackDriver")
+            kwargs = dict(username = account.openstack.key,
+                          password = account.openstack.secret,
+                          auth_url = account.openstack.auth_url,
+                          project = account.openstack.tenant,
+                          cert_validate = account.openstack.cert_validate,
+                          user_domain = account.openstack.user_domain,
+                          project_domain = account.openstack.project_domain,
+                          region = account.openstack.region)
+            drv = RwsdnAccountDriver(self.log, **kwargs)
+            self._account_drivers[account.name] = drv
+            return drv.driver
+        else:
+            return self._account_drivers[account.name].driver    
+
+    @rwstatus
+    def do_init(self, rwlog_ctx):
+        self._rwlog_handler = rwlogger.RwLogger(
+                category="rw-cal-log",
+                subcategory="openstack",
+                log_hdl=rwlog_ctx,
+                )
+        self.log.addHandler(self._rwlog_handler)
+        self.log.propagate = False
+
+    @rwstatus(ret_on_failure=[None])
+    def do_validate_sdn_creds(self, account):
+        """
+        Validates the sdn account credentials for the specified account.
+        Performs an access to the resources using Keystone API. If creds
+        are not valid, returns an error code & reason string
+
+        @param account - a SDN account
+
+        Returns:
+            Validation Code and Details String
+        """
+        status = RwsdnYang.SdnConnectionStatus()
+        drv = self._use_driver(account)
+        try:
+            drv.validate_account_creds()
+
+        except openstack_drv.ValidationError as e:
+            self.log.error("SdnOpenstackPlugin: OpenstackDriver credential validation failed. Exception: %s", str(e))
+            status.status = "failure"
+            status.details = "Invalid Credentials: %s" % str(e)
+
+        except Exception as e:
+            msg = "SdnOpenstackPlugin: OpenstackDriver connection failed. Exception: %s" %(str(e))
+            self.log.error(msg)
+            status.status = "failure"
+            status.details = msg
+
+        else:
+            status.status = "success"
+            status.details = "Connection was successful"
+
+        return status
+
+    @rwstatus(ret_on_failure=[""])
+    def do_create_vnffg_chain(self, account,vnffg):
+        """
+        Creates Service Function chain in ODL
+
+        @param account - a SDN account
+
+        """
+        self.log.debug('Received Create VNFFG chain for account {}, chain {}'.format(account,vnffg))
+        drv = self._use_driver(account)
+        port_list = list()
+        vnf_chain_list = sorted(vnffg.vnf_chain_path, key = lambda x: x.order)
+        prev_vm_id = None 
+        for path in vnf_chain_list:
+            if prev_vm_id and path.vnfr_ids[0].vdu_list[0].vm_id == prev_vm_id:
+                prev_entry = port_list.pop()
+                port_list.append((prev_entry[0],path.vnfr_ids[0].vdu_list[0].port_id))
+                prev_vm_id = None
+            else:
+                prev_vm_id = path.vnfr_ids[0].vdu_list[0].vm_id
+                port_list.append((path.vnfr_ids[0].vdu_list[0].port_id,path.vnfr_ids[0].vdu_list[0].port_id))
+        vnffg_id = drv.create_port_chain(vnffg.name,port_list)
+        return vnffg_id
+
+    @rwstatus
+    def do_terminate_vnffg_chain(self, account,vnffg_id):
+        """
+        Terminate Service Function chain in ODL
+
+        @param account - a SDN account
+        """
+        self.log.debug('Received terminate VNFFG chain for id %s ', vnffg_id)
+        drv = self._use_driver(account)
+        drv.delete_port_chain(vnffg_id)
+
+    @rwstatus(ret_on_failure=[None])
+    def do_create_vnffg_classifier(self, account, vnffg_classifier):
+        """
+           Add VNFFG Classifier 
+
+           @param account - a SDN account
+        """
+        self.log.debug('Received Create VNFFG classifier for account {}, classifier {}'.format(account,vnffg_classifier))
+        protocol_map = {1:'ICMP',6:'TCP',17:'UDP'}
+        flow_classifier_list = list()
+        drv =  self._use_driver(account)
+        for rule in vnffg_classifier.match_attributes:
+            classifier_name = vnffg_classifier.name + '_' + rule.name
+            flow_dict = {} 
+            for field, value in rule.as_dict().items():
+                 if field == 'ip_proto':
+                     flow_dict['protocol'] = protocol_map.get(value,None)
+                 elif field == 'source_ip_address':
+                     flow_dict['source_ip_prefix'] = value
+                 elif field == 'destination_ip_address':
+                     flow_dict['destination_ip_prefix'] = value
+                 elif field == 'source_port':
+                     flow_dict['source_port_range_min'] = value
+                     flow_dict['source_port_range_max'] = value
+                 elif field == 'destination_port':
+                     flow_dict['destination_port_range_min'] = value
+                     flow_dict['destination_port_range_max'] = value
+            if vnffg_classifier.has_field('port_id'):
+                    flow_dict['logical_source_port'] = vnffg_classifier.port_id 
+            flow_classifier_id = drv.create_flow_classifer(classifier_name, flow_dict)
+            flow_classifier_list.append(flow_classifier_id)
+        drv.update_port_chain(vnffg_classifier.rsp_id,flow_classifier_list)
+        return flow_classifier_list
+
+    @rwstatus(ret_on_failure=[None])
+    def do_terminate_vnffg_classifier(self, account, vnffg_classifier_list):
+        """
+           Add VNFFG Classifier 
+
+           @param account - a SDN account
+        """
+        self.log.debug('Received terminate VNFFG classifier for id %s ', vnffg_classifier_list)
+        drv = self._use_driver(account)
+        for classifier_id in vnffg_classifier_list:
+            drv.delete_flow_classifier(classifier_id)
+
+    @rwstatus(ret_on_failure=[None])
+    def do_get_vnffg_rendered_paths(self, account):
+        """
+           Get Rendered Service Path List (SFC)
+
+           @param account - a SDN account
+        """
+        self.log.debug('Received get VNFFG rendered path for account %s ', account)
+        vnffg_rsps = RwsdnYang.VNFFGRenderedPaths() 
+        drv = self._use_driver(account)
+        port_chain_list = drv.get_port_chain_list()
+        for port_chain in port_chain_list:
+            #rsp = vnffg_rsps.vnffg_rendered_path.add()
+            #rsp.name = port_chain['name']
+            pass
+        return vnffg_rsps
+
+
index 5ea2eb0..8371ab8 100644 (file)
@@ -90,6 +90,7 @@ module rwsdn
       enum odl;
       enum mock;
       enum sdnsim;
+      enum openstack;
     }
   }
 
@@ -144,6 +145,62 @@ module rwsdn
           default "rwsdn_sim";
         }
       }
+
+      container openstack {
+        leaf key {
+          type string;
+          mandatory true;
+        }
+
+        leaf secret {
+          type string;
+          mandatory true;
+        }
+
+        leaf auth_url {
+          type string;
+          mandatory true;
+        }
+
+        leaf tenant {
+          type string;
+          mandatory true;
+        }
+
+        leaf admin {
+          type boolean;
+          default false;
+        }
+
+        leaf user-domain {
+          type string;
+          default "Default";
+          description "Domain of the OpenStack user";
+        }
+
+        leaf project-domain {
+          type string;
+          default "Default";
+          description "Domain of the OpenStack project";
+        }
+
+        leaf region {
+          type string;
+          default "RegionOne";
+        }
+
+        leaf plugin-name {
+          type string;
+          default "rwsdn_openstack";
+        }
+
+        leaf cert-validate {
+          type boolean;
+          default false;
+          description "Certificate validatation policy in case of SSL/TLS connection";
+        }
+      }
+
     }
   }
 
@@ -327,6 +384,9 @@ module rwsdn
       leaf rsp-name {
         type string;
       }
+      leaf rsp-id {
+        type yang:uuid;
+      }
       leaf port-id {
         rwpb:field-inline "true";
         rwpb:field-string-max 64;