From: velandy Date: Mon, 13 Mar 2017 15:35:35 +0000 (+0100) Subject: Merge "Commit to master [RIFT 15737] Remove logic for deletion of folder on deletion... X-Git-Tag: v2.0.0~42 X-Git-Url: https://osm.etsi.org/gitweb/?a=commitdiff_plain;h=073a4a7f0a71d23ff18a97c50aa122871a07581f;hp=57c99722a731df6c9541423ff7a1f05b0fe18c86;p=osm%2FSO.git Merge "Commit to master [RIFT 15737] Remove logic for deletion of folder on deletion of descriptor catalog in UI. Protect against a null catalog name that comes when a field in catalog is updated." --- diff --git a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py index 500f10cb..ffab0147 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py +++ b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/openstack_drv.py @@ -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() diff --git a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/portchain/portchain_drv.py b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/portchain/portchain_drv.py index 9dd959d4..78e805d6 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/portchain/portchain_drv.py +++ b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/portchain/portchain_drv.py @@ -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): diff --git a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py index 55ece65c..fc053c22 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py +++ b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/compute.py @@ -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 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 not specified for volume: %s", + volume.name) + raise VolumeValidateError("Mandatory field 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'] diff --git a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/network.py b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/network.py index 8e5107bf..8e6f6087 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/network.py +++ b/rwcal/plugins/vala/rwcal_openstack/rift/rwcal/openstack/utils/network.py @@ -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 diff --git a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py index 52bd574c..eac0d6c9 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py +++ b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py @@ -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 - - diff --git a/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt b/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt index 895ee850..4865d2e7 100644 --- a/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt +++ b/rwlaunchpad/plugins/rwvns/vala/CMakeLists.txt @@ -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 index 00000000..fcf944fb --- /dev/null +++ b/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/CMakeLists.txt @@ -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 index 00000000..ff5d019c --- /dev/null +++ b/rwlaunchpad/plugins/rwvns/vala/rwsdn_openstack/rwsdn_openstack.py @@ -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 + + diff --git a/rwlaunchpad/plugins/rwvns/yang/rwsdn.yang b/rwlaunchpad/plugins/rwvns/yang/rwsdn.yang index 5ea2eb0e..8371ab8f 100644 --- a/rwlaunchpad/plugins/rwvns/yang/rwsdn.yang +++ b/rwlaunchpad/plugins/rwvns/yang/rwsdn.yang @@ -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;