def test_get_switch_device_match(self): self.assertEqual( device_utils.get_switch_device(self.devices, switch_info='A'), self.devices['A']) self.assertEqual( device_utils.get_switch_device( self.devices, ngs_mac_address='aa:bb:cc:dd:ee:ff'), self.devices['B'])
def test_get_switch_device_fallback_to_switch_info(self): self.assertEqual( self.devices['A'], device_utils.get_switch_device( self.devices, switch_info='A', ngs_mac_address='11:22:33:44:55:77'))
def _unplug_port_from_network(self, port, network): """Unplug a port from a network. If the configuration required to unplug the port is not present (e.g. local link information), the port will not be unplugged and no exception will be raised. :param port: The port to unplug :param network: The network from which to unplug the port """ binding_profile = port['binding:profile'] local_link_information = binding_profile.get('local_link_information') local_group_information = binding_profile.get( 'local_group_information') if not local_link_information: return if local_group_information: self._lag_alter_local_link(local_group_information, local_link_information) for switch in local_link_information: switch_info = switch.get('switch_info') switch_id = switch.get('switch_id') switch_device = device_utils.get_switch_device( self.switches, switch_info=switch_info, ngs_mac_address=switch_id) if not switch_device: return port_id = local_link_information[0].get('port_id') # If segmentation ID is None, set vlan 1 segmentation_id = network.get('provider:segmentation_id') or '1' LOG.debug("Unplugging port {port} on {switch_info} from vlan: " "{segmentation_id}".format( port=port_id, switch_info=switch_info, segmentation_id=segmentation_id)) try: if self._is_trunk( port) and switch_device.get_trunk_mode() == "dynamic": switch_device.unset_trunk(port_id, segmentation_id) else: switch_device.delete_port(port_id, segmentation_id) except Exception as e: LOG.error( "Failed to unplug port %(port_id)s " "on device: %(switch)s from network %(net_id)s " "reason: %(exc)s", { 'port_id': port['id'], 'net_id': network['id'], 'switch': switch_info, 'exc': e }) raise e LOG.info( 'Port %(port_id)s has been unplugged from network ' '%(net_id)s on device %(device)s', { 'port_id': port['id'], 'net_id': network['id'], 'device': switch_info })
def _bind_port_to_switch(self, context, port, switch): switch_info = switch.get('switch_info') switch_id = switch.get('switch_id') switch_device = device_utils.get_switch_device( self.switches, switch_info=switch_info, ngs_mac_address=switch_id) if not switch: return network = context.network.current net_type = network['provider:network_type'] if net_type != 'vlan': raise Exception( "Provider network type was {} but shoule be 'vlan'".format( net_type)) physnet = network['provider:physical_network'] switch_physnets = switch_device._get_physical_networks() if switch_physnets and physnet not in switch_physnets: LOG.error( "Cannot bind port %(port)s as device %(device)s is " "not on physical network %(physnet)", { 'port_id': port['id'], 'device': switch_info, 'physnet': physnet }) return port_id = switch.get('port_id') segments = context.segments_to_bind # If segmentation ID is None, set vlan 1 segmentation_id = segments[0].get('segmentation_id') or '1' provisioning_blocks.add_provisioning_component(context._plugin_context, port['id'], resources.PORT, GENERIC_SWITCH_ENTITY) LOG.debug("Putting port {port} on {switch_info} to vlan: " "{segmentation_id}".format(port=port_id, switch_info=switch_info, segmentation_id=segmentation_id)) if self._is_trunk( port) and switch_device.get_trunk_mode() == 'dynamic': trunk_details = port['trunk_details'] self._setup_trunk(switch_device, network, trunk_details, port_id) else: # Move port to network switch_device.plug_port_to_network(port_id, segmentation_id) LOG.info( "Successfully bound port %(port_id)s in segment " "%(segment_id)s on device %(device)s", { 'port_id': port['id'], 'device': switch_info, 'segment_id': segmentation_id }) context.set_binding(segments[0][api.ID], portbindings.VIF_TYPE_OTHER, {})
def _unplug_port_from_network(self, port, network): """Unplug a port from a network. If the configuration required to unplug the port is not present (e.g. local link information), the port will not be unplugged and no exception will be raised. :param port: The port to unplug :param network: The network from which to unplug the port """ binding_profile = port['binding:profile'] local_link_information = binding_profile.get('local_link_information') if not local_link_information: return switch_info = local_link_information[0].get('switch_info') switch_id = local_link_information[0].get('switch_id') switch = device_utils.get_switch_device(self.switches, switch_info=switch_info, ngs_mac_address=switch_id) if not switch: return port_id = local_link_information[0].get('port_id') segmentation_id = network.get('provider:segmentation_id', '1') LOG.debug("Unplugging port {port} on {switch_info} from vlan: " "{segmentation_id}".format(port=port_id, switch_info=switch_info, segmentation_id=segmentation_id)) try: if hasattr(devices, 'corsa_devices') and isinstance( switch, devices.corsa_devices.corsa2100.CorsaDP2100): switch.delete_port(port_id, segmentation_id, vfc_host=self.vfcHost) else: switch.delete_port(port_id, segmentation_id) except Exception as e: LOG.error( "Failed to unplug port %(port_id)s " "on device: %(switch)s from network %(net_id)s " "reason: %(exc)s", { 'port_id': port['id'], 'net_id': network['id'], 'switch': switch_info, 'exc': e }) raise e LOG.info( 'Port %(port_id)s has been unplugged from network ' ' %(net_id)s on device %(device)s', { 'port_id': port['id'], 'net_id': network['id'], 'device': switch_info })
def _unplug_port_from_network(self, port, network): """Unplug a port from a network. If the configuration required to unplug the port is not present (e.g. local link information), the port will not be unplugged and no exception will be raised. :param port: The port to unplug :param network: The network from which to unplug the port """ binding_profile = port['binding:profile'] local_link_information = binding_profile.get('local_link_information') if not local_link_information: return switch_info = local_link_information[0].get('switch_info') switch_id = local_link_information[0].get('switch_id') switch = device_utils.get_switch_device(self.switches, switch_info=switch_info, ngs_mac_address=switch_id) if not switch: return port_id = local_link_information[0].get('port_id') # If segmentation ID is None, set vlan 1 segmentation_id = network.get('provider:segmentation_id') or 1 LOG.debug( "Unplugging port %(port)s on %(switch_info)s from vlan: " "%(segmentation_id)s", { 'port': port_id, 'switch_info': switch_info, 'segmentation_id': segmentation_id }) try: switch.delete_port(port_id, segmentation_id) except Exception as e: LOG.error( "Failed to unplug port %(port_id)s " "on device: %(switch)s from network %(net_id)s " "reason: %(exc)s", { 'port_id': port['id'], 'net_id': network['id'], 'switch': switch_info, 'exc': e }) raise e LOG.info( 'Port %(port_id)s has been unplugged from network ' '%(net_id)s on device %(device)s', { 'port_id': port['id'], 'net_id': network['id'], 'device': switch_info })
def update_port_postcommit(self, context): """Update a port. :param context: PortContext instance describing the new state of the port, as well as the original state prior to the update_port call. Called after the transaction completes. Call can block, though will block the entire process so care should be taken to not drastically affect performance. Raising an exception will result in the deletion of the resource. update_port_postcommit is called for all changes to the port state. It is up to the mechanism driver to ignore state or state changes that it does not know or care about. """ port = context.current if self._is_port_bound(port): binding_profile = port['binding:profile'] local_link_information = binding_profile.get( 'local_link_information') local_group_information = binding_profile.get( 'local_group_information') if not local_link_information: return if local_group_information: self._lag_alter_local_link(local_group_information, local_link_information) for switch in local_link_information: switch_info = switch.get('switch_info') switch_id = switch.get('switch_id') switch_device = device_utils.get_switch_device( self.switches, switch_info=switch_info, ngs_mac_address=switch_id) if not switch_device: return provisioning_blocks.provisioning_complete( context._plugin_context, port['id'], resources.PORT, GENERIC_SWITCH_ENTITY) elif self._is_port_bound(context.original): # The port has been unbound. This will cause the local link # information to be lost, so remove the port from the network on # the switch now while we have the required information. self._unplug_port_from_network(context.original, context.network.current)
def _process_subport_payload(self, payload): parent_port = payload.parent_port binding_profile = parent_port.get('binding:profile') trunk_details = parent_port.get('trunk_details') network = payload.network if not trunk_details: raise Exception("Parent port {} is not trunked".format( parent_port.id)) local_link_information = binding_profile['local_link_information'] local_group_information = binding_profile.get( 'local_group_information') if local_group_information: self._lag_alter_local_link(local_group_information, local_link_information) switches = [] for local_link in local_link_information: port_id = local_link.get('port_id') switch_info = local_link.get('switch_info') switch_id = local_link.get('switch_id') switch = device_utils.get_switch_device(self.switches, switch_info=switch_info, ngs_mac_address=switch_id) switches.append(switch) changed_seg_ids = [ subport['segmentation_id'] for subport in payload.subports ] native_vlan = network['provider:segmentation_id'] seg_ids = [ sub_port['segmentation_id'] for sub_port in trunk_details["sub_ports"] ] return switches, changed_seg_ids, port_id, seg_ids, native_vlan
def bind_port(self, context): """Attempt to bind a port. :param context: PortContext instance describing the port This method is called outside any transaction to attempt to establish a port binding using this mechanism driver. Bindings may be created at each of multiple levels of a hierarchical network, and are established from the top level downward. At each level, the mechanism driver determines whether it can bind to any of the network segments in the context.segments_to_bind property, based on the value of the context.host property, any relevant port or network attributes, and its own knowledge of the network topology. At the top level, context.segments_to_bind contains the static segments of the port's network. At each lower level of binding, it contains static or dynamic segments supplied by the driver that bound at the level above. If the driver is able to complete the binding of the port to any segment in context.segments_to_bind, it must call context.set_binding with the binding details. If it can partially bind the port, it must call context.continue_binding with the network segments to be used to bind at the next lower level. If the binding results are committed after bind_port returns, they will be seen by all mechanism drivers as update_port_precommit and update_port_postcommit calls. But if some other thread or process concurrently binds or updates the port, these binding results will not be committed, and update_port_precommit and update_port_postcommit will not be called on the mechanism drivers with these results. Because binding results can be discarded rather than committed, drivers should avoid making persistent state changes in bind_port, or else must ensure that such state changes are eventually cleaned up. Implementing this method explicitly declares the mechanism driver as having the intention to bind ports. This is inspected by the QoS service to identify the available QoS rules you can use with ports. """ port = context.current binding_profile = port['binding:profile'] local_link_information = binding_profile.get('local_link_information') if self._is_port_supported(port) and local_link_information: switch_info = local_link_information[0].get('switch_info') switch_id = local_link_information[0].get('switch_id') switch = device_utils.get_switch_device(self.switches, switch_info=switch_info, ngs_mac_address=switch_id) if not switch: return network = context.network.current physnet = network['provider:physical_network'] switch_physnets = switch._get_physical_networks() if switch_physnets and physnet not in switch_physnets: LOG.error( "Cannot bind port %(port)s as device %(device)s is " "not on physical network %(physnet)", { 'port_id': port['id'], 'device': switch_info, 'physnet': physnet }) return port_id = local_link_information[0].get('port_id') segments = context.segments_to_bind # If segmentation ID is None, set vlan 1 segmentation_id = segments[0].get('segmentation_id') or '1' provisioning_blocks.add_provisioning_component( context._plugin_context, port['id'], resources.PORT, GENERIC_SWITCH_ENTITY) LOG.debug("Putting port {port} on {switch_info} to vlan: " "{segmentation_id}".format( port=port_id, switch_info=switch_info, segmentation_id=segmentation_id)) # Move port to network switch.plug_port_to_network(port_id, segmentation_id) LOG.info( "Successfully bound port %(port_id)s in segment " "%(segment_id)s on device %(device)s", { 'port_id': port['id'], 'device': switch_info, 'segment_id': segmentation_id }) context.set_binding(segments[0][api.ID], portbindings.VIF_TYPE_OTHER, {})
def test_get_switch_device_no_match(self): self.assertIsNone( device_utils.get_switch_device(self.devices, switch_info='C')) self.assertIsNone( device_utils.get_switch_device( self.devices, ngs_mac_address='11:22:33:44:55:66'))
def test_get_switch_device_match_mac_ignore_case(self): self.assertEqual( device_utils.get_switch_device( self.devices, switch_info='A', ngs_mac_address='AA:BB:CC:DD:EE:FF'), self.devices['B'])
def bind_port(self, context): """Attempt to bind a port. :param context: PortContext instance describing the port This method is called outside any transaction to attempt to establish a port binding using this mechanism driver. Bindings may be created at each of multiple levels of a hierarchical network, and are established from the top level downward. At each level, the mechanism driver determines whether it can bind to any of the network segments in the context.segments_to_bind property, based on the value of the context.host property, any relevant port or network attributes, and its own knowledge of the network topology. At the top level, context.segments_to_bind contains the static segments of the port's network. At each lower level of binding, it contains static or dynamic segments supplied by the driver that bound at the level above. If the driver is able to complete the binding of the port to any segment in context.segments_to_bind, it must call context.set_binding with the binding details. If it can partially bind the port, it must call context.continue_binding with the network segments to be used to bind at the next lower level. If the binding results are committed after bind_port returns, they will be seen by all mechanism drivers as update_port_precommit and update_port_postcommit calls. But if some other thread or process concurrently binds or updates the port, these binding results will not be committed, and update_port_precommit and update_port_postcommit will not be called on the mechanism drivers with these results. Because binding results can be discarded rather than committed, drivers should avoid making persistent state changes in bind_port, or else must ensure that such state changes are eventually cleaned up. Implementing this method explicitly declares the mechanism driver as having the intention to bind ports. This is inspected by the QoS service to identify the available QoS rules you can use with ports. """ port = context.current binding_profile = port['binding:profile'] local_link_information = binding_profile.get('local_link_information') local_group_information = binding_profile.get( 'local_group_information') LOG.debug(local_link_information) LOG.debug(local_group_information) #if self._is_port_supported(port) and local_link_information: if local_link_information: switch_info = local_link_information[0].get('switch_info') switch_id = local_link_information[0].get('switch_id') switch = device_utils.get_switch_device(self.switches, switch_info=switch_info, ngs_mac_address=switch_id) if not switch: return port_id = local_link_information[0].get('port_id') segments = context.segments_to_bind segmentation_id = segments[0].get('segmentation_id') # If segmentation ID is None, set vlan 1 if not segmentation_id: segmentation_id = '1' provisioning_blocks.add_provisioning_component( context._plugin_context, port['id'], resources.PORT, GENERIC_SWITCH_ENTITY) LOG.debug("Putting port {port} on {switch_info} to vlan: " "{segmentation_id}".format( port=port_id, switch_info=switch_info, segmentation_id=segmentation_id)) # If a request to create dynamic group is arrived if (len(local_link_information) > 1): #output = switch.create_port_channel(local_group_information.get('name'),segmentation_id) switch.vlan_configuration(segmentation_id) switch.create_port_channel(local_group_information.get('name'), segmentation_id) port_number_id = re.sub('.*?([0-9]*)$', r'\1', local_group_information.get('name')) #output = switch.create_port_channel(segmentation_id) LOG.debug("YYY: {port_number_id}".format( port_number_id=port_number_id)) for item in local_link_information: i = item.get('port_id') LOG.debug("XXX: {output}".format(output=i)) switch.configure_port_channel(i, port_number_id, segmentation_id) #port_list = switch.show_port_list().splitlines() #port_list = [w.replace('interface Port-channel', '') for w in port_list] #for item in port_list: # LOG.debug("YYY: {output}".format(output=item)) else: # Move port to network switch.plug_port_to_network(port_id, segmentation_id) LOG.info( "Successfully bound port %(port_id)s in segment " " %(segment_id)s on device %(device)s", { 'port_id': port['id'], 'device': switch_info, 'segment_id': segmentation_id }) context.set_binding(segments[0][driver_api.ID], portbindings.VIF_TYPE_OTHER, {})