def create_network_postcommit(self, context): super(KaloomOVSMechanismDriver, self).create_network_postcommit(context) network_id = context.current.get('id') nw_name = utils._kaloom_nw_name(self.prefix, network_id, context.current.get('name')) #force clean if vfabric network already exists, probably of unsuccessful delete_network_postcommit knid_mapping = kaloom_db.get_knid_mapping(network_id=network_id) if knid_mapping: try: self._clean_local_vlan_mappings(network_id) kaloom_db.delete_knid_mapping(network_id) self.kaloom.delete_l2_network(knid_mapping.network_name) except Exception as e: #forced clean: no error raise LOG.warning( "create_network_postcommit: Error on clearing overlapping vfabric network=%s errors: %s", knid_mapping.network_name, e) #create nw in vfabric try: knid = self.kaloom.create_l2_network( nw_name, kconst.DEFAULT_VLAN_ID).get('kaloom_knid') except Exception as e: LOG.error("errors: %s", e) #raising an exception will result in rollback of the transaction raise ml2_exc.MechanismDriverError( method='create_network_postcommit', errors=e) LOG.info("Created Kaloom network with KNID %d " % knid) kaloom_db.create_knid_mapping(kaloom_knid=knid, network_id=network_id, network_name=nw_name)
def _set_external_gateway(self, context, router_id, network_id, original_router, new_router): try: nw_name = utils._kaloom_nw_name(self.prefix, network_id) except n_exc.NetworkNotFound as e: msg = ('can not _set_external_gateway as no such network=%s, msg:%s' % (network_id, e)) LOG.error(msg) return core = directory.get_plugin() ip_address = new_router['external_gateway_info']['external_fixed_ips'][0]['ip_address'] subnet_id = new_router['external_gateway_info']['external_fixed_ips'][0]['subnet_id'] subnet = core.get_subnet(context, subnet_id) # Package all the info needed for vFabric programming router_info = copy.deepcopy(new_router) router_info['nw_name'] = nw_name router_info['subnet_id'] = subnet_id router_info['ip_address'] = ip_address router_info['cidr'] = subnet['cidr'] router_info['gip'] = subnet['gateway_ip'] router_info['ip_version'] = subnet['ip_version'] self.driver.add_router_interface(context, router_info) self._update_port_up(context, new_router['gw_port_id'])
def _unset_external_gateway(self, context, router_id, router_unset_ext_gw): # Get ip_address info for the subnet that is being deleted from the router. original_router = self.get_router(context, router_id) core = directory.get_plugin() if original_router['external_gateway_info'] is None or len(original_router['external_gateway_info']['external_fixed_ips']) == 0: ##nothing to unset new_router = super(KaloomL3ServicePlugin, self).update_router(context, router_id, router_unset_ext_gw) return new_router ip_address = original_router['external_gateway_info']['external_fixed_ips'][0]['ip_address'] subnet_id = original_router['external_gateway_info']['external_fixed_ips'][0]['subnet_id'] subnet = core.get_subnet(context, subnet_id) # Update router DB new_router = super(KaloomL3ServicePlugin, self).update_router(context, router_id, router_unset_ext_gw) # Get network information of the gateway subnet that is being removed for vFabric programming network_id = subnet['network_id'] try: nw_name = utils._kaloom_nw_name(self.prefix, network_id) except n_exc.NetworkNotFound as e: LOG.warning('can not _unset_external_gateway as no such network=%s, msg:%s', network_id, e) return new_router router_info = copy.deepcopy(new_router) router_info['nw_name'] = nw_name router_info['subnet_id'] = subnet_id router_info['ip_address'] = ip_address router_info['ip_version'] = subnet['ip_version'] self.driver.remove_router_interface(context, router_info) return new_router
def create_network_postcommit(self, context): super(KaloomOVSMechanismDriver, self).create_network_postcommit(context) #not allowed network_type if not self._is_network_type_allowed(context): return network_id = context.current.get('id') nw_name = utils._kaloom_nw_name(self.prefix, network_id) #create nw in vfabric gui_nw_name = utils._kaloom_gui_nw_name(self.prefix, network_id, context.current.get('name')) try: knid = self.vfabric.create_l2_network( nw_name, gui_nw_name, kconst.DEFAULT_VLAN_ID).get('kaloom_knid') except Exception as e: ##duplicate should not raise error (TYPE_KNID is used by both kaloom_ovs and kaloom_kvs mech driver) if "unique duplicate constraint" in str(e): return else: LOG.error("errors: %s", e) #raising an exception will result in rollback of the transaction raise ml2_exc.MechanismDriverError( method='create_network_postcommit', errors=e) LOG.info("Created Kaloom network with KNID %d " % knid) kaloom_db.create_knid_mapping(kaloom_knid=knid, network_id=network_id)
def cleanup(self): LOG.debug('cleanup..') #clean stranded networks in vfabric, that does not exist in openstack: e.g. created after netconf timeout, manually created. try: networks = kaloom_db.get_networks() openstack_nw_names = [] for network in networks: nw_name = utils._kaloom_nw_name(self.prefix, network.id) openstack_nw_names.append(nw_name) vfabric_nw_names = self.vfabric.get_l2_network_names(self.prefix) stranded_nw_names = set(vfabric_nw_names) - set(openstack_nw_names) if len(stranded_nw_names) > 0: LOG.info("cleanup found stranded networks: %s, cleaning up..", stranded_nw_names) for stranded_nw_name in stranded_nw_names: try: network_id = stranded_nw_name.split(self.prefix)[1].split( '_')[0] #network_id is in between of prefix and _ knid_mapping = kaloom_db.get_knid_mapping( network_id=network_id) if knid_mapping: kaloom_db.delete_knid_mapping(network_id) clean_local_vlan_mappings(network_id) self.vfabric.delete_l2_network(stranded_nw_name) except Exception as e: LOG.warning( "cleanup failed to delete stranded l2_network:%s in vfabric, err:%s", stranded_nw_name, e) except Exception as e: LOG.warning("cleanup stranded networks: error caught err_msg:%s", e) #clean stranded tp-attachment try: all_stale_vlan_mappings = kaloom_db.get_stale_vlan_mappings( self.creating_seconds, self.deleting_seconds) except Exception as e: LOG.warning( "cleanup stranded tp-attachment: error caught err_msg:%s", e) return if all_stale_vlan_mappings: ctx = nctx.get_admin_context() for m in all_stale_vlan_mappings: try: LOG.info( 'Cleaning.. for host=%s network=%s vlan=%s state=%s timestamp:%s', m.host, m.network_id, m.vlan_id, m.state, m.timestamp) tp = self.vfabric.get_tp_by_annotation(m.host) if tp: self.vfabric.detach_tp_from_l2_network( m.network_name, tp.get('id')) remove_local_segment(ctx, m.segment_id) remove_local_vlan_mapping(m) except Exception as e: LOG.warning( "cleanup: error caught for host=%s, network=%s err_msg:%s", m.host, m.network_id, e)
def _create_local_vlan_mapping(self, context, call_from): try: network_id = context.current.get('network_id') host = context.host getattr(super(KaloomOVSMechanismDriver, self), call_from)(context) kvs_present = self._is_kvs_agent_present(context) ovs_present = self._is_ovs_agent_present(context) if kvs_present: #kaloom_ovs plugin found kvs_present, nothing to do. return if not ovs_present: LOG.warning( "%s: KVS/OVS agent is not alive on host=%s, nothing to do.", call_from, host) #don't raise to pass tempest test_portbinding_bumps_revision return #in case of ovs node binding. LOG.info("%s: Found OVS type agent, Using OVS specific logic", call_from) nw_name = utils._kaloom_nw_name(self.prefix, network_id) local_vlan_mapping = kaloom_db.get_vlan_mapping_for_network_and_host( network_id, host) if local_vlan_mapping is None: physical_network = kaloom_db.get_segment_for_network( network_id ).physical_network #None segment raises exception #There is no possibility of concurrent tp_detach on delete_port_postcommit, as there is no remaining local_vlan_mapping. #lock concurrent attach/detach for vfabric TP if not utils.tp_operation_lock(host, network_id): raise ValueError('Could not get lock.') try: #default is state: 'CREATING' local_vlan_mapping = self._allocate_local_vlan_mapping( context, network_id, host, physical_network, nw_name) except: pass finally: utils.tp_operation_unlock(host, network_id) #release lock if local_vlan_mapping is None: msg = 'Vlan could not be allocated.' raise ValueError(msg) #There could be concurrent ports deletion leading to tp_detach. elif local_vlan_mapping.state == 'DELETING': #DELETING: deletion ongoing or never finished. msg = 'vlan mapping state:%s exists, another concurrent tp_detach ongoing' % local_vlan_mapping.state raise ValueError(msg) #or concurrent ports creation leading to tp_attach. elif local_vlan_mapping.state == 'CREATING': #CREATING: another concurrent tp_attach not finished yet or never finished: try_to_bind anyway (duplicate tp_attach will be handeled.) pass return except Exception as e: LOG.error("Error during %s on host=%s, network=%s, err_msg:%s", call_from, host, network_id, e) raise ml2_exc.MechanismDriverError(method=call_from, errors=e) #rollback.
def add_router_interface(self, context, router_id, interface_info): """Add a subnet of a network to an existing router.""" router = self.get_router(context, router_id) # add_router_interface can't go in parallel with l3_sync_interface on same router # parallelism of router operations (on different router) # write (x) lock on "the router" during transaction. db_session = db_api.get_writer_session() with db_session.begin(subtransactions=True): caller_msg = 'add_router_interface on router id=%s name=%s' % (router_id, router['name']) kaloom_db.get_Lock(db_session, router_id, read=False, caller_msg = caller_msg) new_router_ifc = super(KaloomL3ServicePlugin, self).add_router_interface( context, router_id, interface_info) core = directory.get_plugin() # Get network info for the subnet that is being added to the router. # Check if the interface information is by port-id or subnet-id add_by_port, add_by_sub = self._validate_interface_info(interface_info) if add_by_sub: subnet = core.get_subnet(context, interface_info['subnet_id']) port = core.get_port(context, new_router_ifc['port_id']) #port has multiple (ip_address, subnet_id) ip_address = self._get_subnet_ip(port['fixed_ips'], interface_info['subnet_id']) elif add_by_port: port = core.get_port(context, interface_info['port_id']) ip_address = port['fixed_ips'][0]['ip_address'] subnet_id = port['fixed_ips'][0]['subnet_id'] subnet = core.get_subnet(context, subnet_id) # Package all the info needed for vFabric programming network_id = subnet['network_id'] try: nw_name = utils._kaloom_nw_name(self.prefix, network_id) except n_exc.NetworkNotFound as e: LOG.warning('Nothing to do in add_router_interface as no such network=%s, msg:%s', network_id, e) return new_router_ifc router_info = copy.deepcopy(new_router_ifc) router_info['nw_name'] = nw_name router_info['ip_address'] = ip_address router_info['name'] = router['name'] router_info['cidr'] = subnet['cidr'] router_info['gip'] = subnet['gateway_ip'] router_info['ip_version'] = subnet['ip_version'] try: self.driver.add_router_interface(context, router_info) self._update_port_up(context, port['id']) return new_router_ifc except Exception: with excutils.save_and_reraise_exception(): super(KaloomL3ServicePlugin, self).remove_router_interface( context, router_id, interface_info)
def remove_router_interface(self, context, router_id, interface_info): """Remove a subnet of a network from an existing router.""" router = self.get_router(context, router_id) # remove_router_interface can't go in parallel with l3_sync_interface on same router # parallelism of router operations (on different router) # write (x) lock on "the router" during transaction. db_session = db_api.get_writer_session() with db_session.begin(subtransactions=True): caller_msg = 'remove_router_interface on router id=%s name=%s' % (router_id, router['name']) kaloom_db.get_Lock(db_session, router_id, read=False, caller_msg = caller_msg) # Get ip_address info for the subnet that is being deleted from the router. # Check if the interface information is by port-id or subnet-id core = directory.get_plugin() add_by_port, add_by_sub = self._validate_interface_info(interface_info) if add_by_sub: subnet = core.get_subnet(context, interface_info['subnet_id']) ip_address = self._get_subnet_ip_from_router_info(router, interface_info['subnet_id'], subnet['network_id'], subnet['gateway_ip']) elif add_by_port: port = core.get_port(context, interface_info['port_id']) ip_address = port['fixed_ips'][0]['ip_address'] subnet_id = port['fixed_ips'][0]['subnet_id'] subnet = core.get_subnet(context, subnet_id) router_ifc_to_del = ( super(KaloomL3ServicePlugin, self).remove_router_interface( context, router_id, interface_info) ) # Get network information of the subnet that is being removed network_id = subnet['network_id'] try: nw_name = utils._kaloom_nw_name(self.prefix, network_id) except n_exc.NetworkNotFound as e: LOG.warning('Nothing to do in remove_router_interface as no such network=%s, msg:%s', network_id, e) return router_info = copy.deepcopy(router_ifc_to_del) router_info['nw_name'] = nw_name router_info['name'] = router['name'] router_info['ip_address'] = ip_address router_info['ip_version'] = subnet['ip_version'] try: self.driver.remove_router_interface(context, router_info) return router_ifc_to_del except Exception as e: msg = "remove_router_interface (router %s -- IP %s subnet_id %s) failed in vfabric: %s" % (router['name'], ip_address, subnet['id'], e) LOG.error(msg)
def update_network_postcommit(self, context): super(KaloomOVSMechanismDriver, self).update_network_postcommit(context) #not allowed network_type if not self._is_network_type_allowed(context): return original_name = context.original.get('name') current_name = context.current.get('name') if current_name != original_name: #nw renamed #rename vfabric network gui-name. network_id = context.current.get('id') nw_name = utils._kaloom_nw_name(self.prefix, network_id) current_gui_nw_name = utils._kaloom_gui_nw_name( self.prefix, network_id, current_name) try: self.vfabric.rename_l2_network(nw_name, current_gui_nw_name) except Exception as e: LOG.error("errors: %s", e)
def delete_network_postcommit(self, context): super(KaloomOVSMechanismDriver, self).delete_network_postcommit(context) # don't check allowed network_type, as network's segments are deleted before this, resulting network_type=None. network_id = context.current.get('id') nw_name = utils._kaloom_nw_name(self.prefix, network_id) try: knid_mapping = kaloom_db.get_knid_mapping(network_id=network_id) if knid_mapping: #rollback on create_network_postcommit calls delete_network so the knid_mapping may not exist kaloom_db.delete_knid_mapping(network_id) clean_local_vlan_mappings(network_id) #vfabric network could have been created after netconf timeout on create_l2_network i.e. even in case of non-existing knid_mapping. self.vfabric.delete_l2_network( nw_name ) #if this fails, cleanup process will take care of stranded vfabric networks. except Exception as e: LOG.warning("delete_network_postcommit network:%s err:%s", nw_name, e) return
def get_router_interfaces(self, r): core = directory.get_plugin() ctx = nctx.get_admin_context() grouped_router_interfaces = {} ports = core.get_ports(ctx, filters={'device_id': [r['id']]}) or [] for p in ports: for fixed_ip in p['fixed_ips']: router_interface = r.copy() subnet_id = fixed_ip['subnet_id'] subnet = core.get_subnet(ctx, subnet_id) network_id = p['network_id'] nw_name = utils._kaloom_nw_name(self.prefix, network_id) router_interface['nw_name'] = nw_name router_interface['ip_address'] = fixed_ip['ip_address'] router_interface['cidr'] = subnet['cidr'] router_interface['gip'] = subnet['gateway_ip'] router_interface['ip_version'] = subnet['ip_version'] router_interface['subnet_id'] = subnet_id if nw_name in grouped_router_interfaces: grouped_router_interfaces[nw_name].append(router_interface) else: grouped_router_interfaces[nw_name] = [router_interface] return grouped_router_interfaces
def _try_to_bind_segment_ovs_agent(self, context, segment, agent): network_id = segment.get('network_id') try: nw_name = utils._kaloom_nw_name( self.prefix, network_id, utils._get_network_name(network_id)) except NetworkNotFound as e: LOG.error(e) return False host = context.current.get('binding:host_id') #lock concurrent attach/detach for vfabric TP if not utils.tp_operation_lock(host, network_id): return False try: local_vlan_mapping = kaloom_db.get_vlan_mapping_for_network_and_host( network_id, host) if local_vlan_mapping: if local_vlan_mapping.stale: LOG.info( 'stale vlan=%s mapping found on host=%s, network=%s, reusing the same', local_vlan_mapping.vlan_id, host, network_id) kaloom_db.update_network_host_vlan_mapping( network_id=network_id, host=host, stale=False) #no more stale else: #in case of None try: tp_id = self.kaloom.get_tp_by_annotation(host).get('id') except Exception as e: msg = "Error on get_tp_by_annotation for host %s: %s" % ( host, e) raise ValueError(msg) attach_name = '%s:%s' % (network_id, tp_id) local_vlan_mapping = self._allocate_local_vlan_mapping( context, network_id, host, segment, nw_name) if local_vlan_mapping is None: msg = 'Vlan could not be allocated.' raise ValueError(msg) local_vlan_id = local_vlan_mapping.vlan_id try: self.kaloom.attach_tp_to_l2_network( nw_name, attach_name, tp_id, local_vlan_id) except Exception as e: msg = "Error on attach_tp_to_l2_network for tpid %s vlan %s nw %s: %s" % ( tp_id, local_vlan_id, nw_name, e) # release vlan and segment that was just created. self._remove_local_segment(context._plugin_context, local_vlan_mapping.segment_id) self._remove_local_vlan_mapping(local_vlan_mapping) raise ValueError(msg) local_vlan_id = local_vlan_mapping.vlan_id local_seg_id = local_vlan_mapping.segment_id context.set_binding(local_seg_id, self.get_vif_type(context, agent, segment), self.get_vif_details(context, agent, segment)) LOG.info("BINDED SEGMENT VLAN %d host %s segment %s " % (local_vlan_id, host, local_seg_id)) return True except Exception as e: LOG.error( "Error during bind_port on host=%s, network=%s, err_msg:%s", host, network_id, e) return False finally: utils.tp_operation_unlock(host, network_id) #release lock
def _try_to_bind_segment_ovs_agent(self, context, segment, agent): network_id = segment.get('network_id') host = context.current.get('binding:host_id') try: nw_name = utils._kaloom_nw_name(self.prefix, network_id) local_vlan_mapping = kaloom_db.get_vlan_mapping_for_network_and_host( network_id, host) #There could be concurrent ports deletion leading to tp_detach if local_vlan_mapping is None: ## None implicitly means 'DELETED' state or doesnot exist msg = 'can not bind as vlan mapping doesnot exist.' raise ValueError(msg) if local_vlan_mapping.state == 'DELETING': #DELETING: deletion ongoing or never finished. msg = 'can not bind as vlan mapping state: %s, deletion ongoing' % local_vlan_mapping.state raise ValueError(msg) if local_vlan_mapping.state == 'CREATING': #concurrent tp_attach has been allowed. try: tp_id = self.vfabric.get_tp_by_annotation(host).get('id') except Exception as e: msg = "Error on get_tp_by_annotation for host %s: %s" % ( host, e) raise ValueError(msg) attach_name = '%s:%s' % (network_id, tp_id) local_vlan_id = local_vlan_mapping.vlan_id try: self.vfabric.attach_tp_to_l2_network( nw_name, attach_name, tp_id, local_vlan_id) except Exception as e: ##duplicate should not raise error. if "unique duplicate constraint" not in str(e): msg = "Error on attach_tp_to_l2_network for tpid %s vlan %s nw %s: %s" % ( tp_id, local_vlan_id, nw_name, e) raise ValueError(msg) local_vlan_id = local_vlan_mapping.vlan_id local_seg_id = local_vlan_mapping.segment_id original_state = local_vlan_mapping.state if original_state == 'CREATING': new_state = 'CREATED' else: new_state = original_state if new_state != original_state: status = kaloom_db.update_state_on_network_host_vlan_mapping( network_id=network_id, host=host, state=new_state) if not status: # concurrent deletion of "last port" already happened. #rollback and raise error self.vfabric.detach_tp_from_l2_network(nw_name, tp_id) raise ValueError('concurrent deletion happened.') ## context.set_binding(local_seg_id, self.get_vif_type(context, agent, segment), self.get_vif_details(context, agent, segment)) LOG.info( "state: %s -> %s, PORT BINDED on SEGMENT=%s, VLAN=%d of host=%s, network=%s" % (original_state, new_state, local_seg_id, local_vlan_id, host, network_id)) return True except Exception as e: LOG.error( "Error during bind_port on OVS host=%s, network=%s, err_msg:%s", host, network_id, e) return False
class KaloomL3DriverTestCase(base.BaseTestCase): PREFIX = '__OpenStack__' DB_CONTEXT = neutron_context.get_admin_context() ROUTER_INFO = {'id': uuidutils.generate_uuid(), 'name': 'router1', 'nw_name': utils._kaloom_nw_name(PREFIX, uuidutils.generate_uuid()), 'subnet_id': uuidutils.generate_uuid(), 'ip_address': '192.168.1.15', 'cidr':'192.168.1.15/24', 'ip_version':'4', 'gip': '192.168.1.1'} ROUTER_NODE_ID = uuidutils.generate_uuid() ROUTER_INTERFACE_INFO_FIRST_TIME = {'node_id': ROUTER_NODE_ID, 'interface': None, 'cidrs': []} ROUTER_INTERFACE_INFO_EXACT_STALE = {'node_id': ROUTER_NODE_ID, 'interface': 'net1', 'cidrs': ['192.168.1.15/24']} ROUTER_INTERFACE_INFO_NON_EXACT_OVERLAPPING = {'node_id': ROUTER_NODE_ID, 'interface': 'net1', 'cidrs': ['192.168.1.16/24']} @patch('networking_kaloom.services.l3.driver.KaloomNetconf',autospec=True) def setUp(self, mock_KaloomNetconf): super(KaloomL3DriverTestCase, self).setUp() self.driver = kaloom_l3_driver.KaloomL3Driver(self.PREFIX) self.mock_KaloomNetconf_instance = mock_KaloomNetconf.return_value def tearDown(self): super(KaloomL3DriverTestCase, self).tearDown() self.mock_KaloomNetconf_instance.reset_mock() LOG.error.reset_mock() def test_add_router_interface_first_time(self): self.mock_KaloomNetconf_instance.get_router_interface_info = Mock(return_value = self.ROUTER_INTERFACE_INFO_FIRST_TIME) self.driver.add_router_interface(self.DB_CONTEXT, self.ROUTER_INFO) self.mock_KaloomNetconf_instance.attach_router.assert_called_once() self.mock_KaloomNetconf_instance.delete_ipaddress_from_interface.assert_not_called() self.mock_KaloomNetconf_instance.add_ipaddress_to_interface.assert_called_once() LOG.error.assert_not_called def test_add_router_interface_exact_stale(self): self.mock_KaloomNetconf_instance.get_router_interface_info = Mock(return_value = self.ROUTER_INTERFACE_INFO_EXACT_STALE) self.driver.add_router_interface(self.DB_CONTEXT, self.ROUTER_INFO) self.mock_KaloomNetconf_instance.attach_router.assert_not_called() self.mock_KaloomNetconf_instance.delete_ipaddress_from_interface.assert_not_called() self.mock_KaloomNetconf_instance.add_ipaddress_to_interface.assert_not_called() LOG.error.assert_not_called def test_add_router_interface_non_exact_overlapping(self): self.mock_KaloomNetconf_instance.get_router_interface_info = Mock(return_value = self.ROUTER_INTERFACE_INFO_NON_EXACT_OVERLAPPING) self.driver.add_router_interface(self.DB_CONTEXT, self.ROUTER_INFO) self.mock_KaloomNetconf_instance.attach_router.assert_not_called() self.mock_KaloomNetconf_instance.delete_ipaddress_from_interface.assert_called_once() self.mock_KaloomNetconf_instance.add_ipaddress_to_interface.assert_called_once() LOG.error.assert_not_called def test_add_router_interface_undo_attach_router(self): self.mock_KaloomNetconf_instance.get_router_interface_info = Mock(return_value = self.ROUTER_INTERFACE_INFO_FIRST_TIME) self.mock_KaloomNetconf_instance.add_ipaddress_to_interface = Mock(side_effect = Exception('Boom!')) try: self.driver.add_router_interface(self.DB_CONTEXT, self.ROUTER_INFO) except: pass self.mock_KaloomNetconf_instance.attach_router.assert_called_once() self.mock_KaloomNetconf_instance.detach_router.assert_called_once()