def _create_port(self, port_client, instance, network_id, port_req_body, fixed_ip=None, security_group_ids=None, available_macs=None, dhcp_opts=None): """Attempts to create a port for the instance on the given network. :param port_client: The client to use to create the port. :param instance: Create the port for the given instance. :param network_id: Create the port on the given network. :param port_req_body: Pre-populated port request. Should have the device_id, device_owner, and any required neutron extension values. :param fixed_ip: Optional fixed IP to use from the given network. :param security_group_ids: Optional list of security group IDs to apply to the port. :param available_macs: Optional set of available MAC addresses to use. :param dhcp_opts: Optional DHCP options. :returns: ID of the created port. :raises PortLimitExceeded: If neutron fails with an OverQuota error. """ try: if fixed_ip: port_req_body['port']['fixed_ips'] = [{'ip_address': fixed_ip}] port_req_body['port']['network_id'] = network_id port_req_body['port']['admin_state_up'] = True port_req_body['port']['tenant_id'] = instance['project_id'] if security_group_ids: port_req_body['port']['security_groups'] = security_group_ids if available_macs is not None: if not available_macs: raise exception.PortNotFree( instance=instance['display_name']) mac_address = available_macs.pop() port_req_body['port']['mac_address'] = mac_address if dhcp_opts is not None: port_req_body['port']['extra_dhcp_opts'] = dhcp_opts port_id = port_client.create_port(port_req_body)['port']['id'] LOG.debug(_('Successfully created port: %s') % port_id, instance=instance) return port_id except neutron_client_exc.NeutronClientException as e: LOG.exception(_('Neutron error creating port on network %s') % network_id, instance=instance) # NOTE(mriedem): OverQuota in neutron is a 409 if e.status_code == 409: raise exception.PortLimitExceeded() raise
def allocate_for_instance(self, context, instance, **kwargs): """Allocate network resources for the instance. :param requested_networks: optional value containing network_id, fixed_ip, and port_id :param security_groups: security groups to allocate for instance :param macs: None or a set of MAC addresses that the instance should use. macs is supplied by the hypervisor driver (contrast with requested_networks which is user supplied). NB: NeutronV2 currently assigns hypervisor supplied MAC addresses to arbitrary networks, which requires openflow switches to function correctly if more than one network is being used with the bare metal hypervisor (which is the only one known to limit MAC addresses). """ hypervisor_macs = kwargs.get('macs', None) available_macs = None if hypervisor_macs is not None: # Make a copy we can mutate: records macs that have not been used # to create a port on a network. If we find a mac with a # pre-allocated port we also remove it from this set. available_macs = set(hypervisor_macs) neutron = neutronv2.get_client(context) LOG.debug(_('allocate_for_instance() for %s'), instance['display_name']) if not instance['project_id']: msg = _('empty project id for instance %s') raise exception.InvalidInput( reason=msg % instance['display_name']) requested_networks = kwargs.get('requested_networks') ports = {} fixed_ips = {} net_ids = [] if requested_networks: for network_id, fixed_ip, port_id in requested_networks: if port_id: port = neutron.show_port(port_id)['port'] if hypervisor_macs is not None: if port['mac_address'] not in hypervisor_macs: raise exception.PortNotUsable(port_id=port_id, instance=instance['display_name']) else: # Don't try to use this MAC if we need to create a # port on the fly later. Identical MACs may be # configured by users into multiple ports so we # discard rather than popping. available_macs.discard(port['mac_address']) network_id = port['network_id'] ports[network_id] = port elif fixed_ip and network_id: fixed_ips[network_id] = fixed_ip if network_id: net_ids.append(network_id) nets = self._get_available_networks(context, instance['project_id'], net_ids) if not nets: LOG.warn(_("No network configured!"), instance=instance) return [] security_groups = kwargs.get('security_groups', []) security_group_ids = [] # TODO(arosen) Should optimize more to do direct query for security # group if len(security_groups) == 1 if len(security_groups): search_opts = {'tenant_id': instance['project_id']} user_security_groups = neutron.list_security_groups( **search_opts).get('security_groups') for security_group in security_groups: name_match = None uuid_match = None for user_security_group in user_security_groups: if user_security_group['name'] == security_group: if name_match: msg = (_("Multiple security groups found matching" " '%s'. Use an ID to be more specific."), security_group) raise exception.NoUniqueMatch(msg) name_match = user_security_group['id'] if user_security_group['id'] == security_group: uuid_match = user_security_group['id'] # If a user names the security group the same as # another's security groups uuid, the name takes priority. if not name_match and not uuid_match: raise exception.SecurityGroupNotFound( security_group_id=security_group) elif name_match: security_group_ids.append(name_match) elif uuid_match: security_group_ids.append(uuid_match) touched_port_ids = [] created_port_ids = [] for network in nets: # If security groups are requested on an instance then the # network must has a subnet associated with it. Some plugins # implement the port-security extension which requires # 'port_security_enabled' to be True for security groups. # That is why True is returned if 'port_security_enabled' # is not found. if (security_groups and not ( network['subnets'] and network.get('port_security_enabled', True))): raise exception.SecurityGroupCannotBeApplied() network_id = network['id'] zone = 'compute:%s' % instance['availability_zone'] port_req_body = {'port': {'device_id': instance['uuid'], 'device_owner': zone}} try: port = ports.get(network_id) self._populate_neutron_extension_values(instance, port_req_body) # Requires admin creds to set port bindings port_client = (neutron if not self._has_port_binding_extension() else neutronv2.get_client(context, admin=True)) if port: port_client.update_port(port['id'], port_req_body) touched_port_ids.append(port['id']) else: fixed_ip = fixed_ips.get(network_id) if fixed_ip: port_req_body['port']['fixed_ips'] = [{'ip_address': fixed_ip}] port_req_body['port']['network_id'] = network_id port_req_body['port']['admin_state_up'] = True port_req_body['port']['tenant_id'] = instance['project_id'] if security_group_ids: port_req_body['port']['security_groups'] = ( security_group_ids) if available_macs is not None: if not available_macs: raise exception.PortNotFree( instance=instance['display_name']) mac_address = available_macs.pop() port_req_body['port']['mac_address'] = mac_address created_port_ids.append( port_client.create_port(port_req_body)['port']['id']) except Exception: with excutils.save_and_reraise_exception(): for port_id in touched_port_ids: try: port_req_body = {'port': {'device_id': None}} # Requires admin creds to set port bindings if self._has_port_binding_extension(): port_req_body['port']['binding:host_id'] = None port_client = neutronv2.get_client( context, admin=True) else: port_client = neutron port_client.update_port(port_id, port_req_body) except Exception: msg = _("Failed to update port %s") LOG.exception(msg, port_id) for port_id in created_port_ids: try: neutron.delete_port(port_id) except Exception: msg = _("Failed to delete port %s") LOG.exception(msg, port_id) nw_info = self._get_instance_nw_info(context, instance, networks=nets) # NOTE(danms): Only return info about ports we created in this run. # In the initial allocation case, this will be everything we created, # and in later runs will only be what was created that time. Thus, # this only affects the attach case, not the original use for this # method. return network_model.NetworkInfo([port for port in nw_info if port['id'] in created_port_ids + touched_port_ids])
def allocate_for_instance(self, context, instance, **kwargs): """Allocate all network resources for the instance. TODO(someone): document the rest of these parameters. :param macs: None or a set of MAC addresses that the instance should use. macs is supplied by the hypervisor driver (contrast with requested_networks which is user supplied). NB: QuantumV2 currently assigns hypervisor supplied MAC addresses to arbitrary networks, which requires openflow switches to function correctly if more than one network is being used with the bare metal hypervisor (which is the only one known to limit MAC addresses). """ hypervisor_macs = kwargs.get('macs', None) available_macs = None if hypervisor_macs is not None: # Make a copy we can mutate: records macs that have not been used # to create a port on a network. If we find a mac with a # pre-allocated port we also remove it from this set. available_macs = set(hypervisor_macs) quantum = quantumv2.get_client(context) LOG.debug(_('allocate_for_instance() for %s'), instance['display_name']) if not instance['project_id']: msg = _('empty project id for instance %s') raise exception.InvalidInput(reason=msg % instance['display_name']) requested_networks = kwargs.get('requested_networks') ports = {} fixed_ips = {} net_ids = [] if requested_networks: for network_id, fixed_ip, port_id in requested_networks: if port_id: port = quantum.show_port(port_id)['port'] if hypervisor_macs is not None: if port['mac_address'] not in hypervisor_macs: raise exception.PortNotUsable( port_id=port_id, instance=instance['display_name']) else: # Don't try to use this MAC if we need to create a # port on the fly later. Identical MACs may be # configured by users into multiple ports so we # discard rather than popping. available_macs.discard(port['mac_address']) network_id = port['network_id'] ports[network_id] = port elif fixed_ip: fixed_ips[network_id] = fixed_ip net_ids.append(network_id) nets = self._get_available_networks(context, instance['project_id'], net_ids) touched_port_ids = [] created_port_ids = [] for network in nets: network_id = network['id'] zone = 'compute:%s' % instance['availability_zone'] port_req_body = { 'port': { 'device_id': instance['uuid'], 'device_owner': zone } } try: port = ports.get(network_id) if port: quantum.update_port(port['id'], port_req_body) touched_port_ids.append(port['id']) else: if fixed_ips.get(network_id): port_req_body['port']['fixed_ips'] = [{ 'ip_address': fixed_ip }] port_req_body['port']['network_id'] = network_id port_req_body['port']['admin_state_up'] = True port_req_body['port']['tenant_id'] = instance['project_id'] if available_macs is not None: if not available_macs: raise exception.PortNotFree( instance=instance['display_name']) mac_address = available_macs.pop() port_req_body['port']['mac_address'] = mac_address created_port_ids.append( quantum.create_port(port_req_body)['port']['id']) except Exception: with excutils.save_and_reraise_exception(): for port_id in touched_port_ids: port_in_server = quantum.show_port(port_id).get('port') if not port_in_server: raise Exception(_('Port not found')) port_req_body = {'port': {'device_id': None}} quantum.update_port(port_id, port_req_body) for port_id in created_port_ids: try: quantum.delete_port(port_id) except Exception as ex: msg = _("Fail to delete port %(portid)s with" " failure: %(exception)s") LOG.debug(msg, { 'portid': port_id, 'exception': ex }) self.trigger_security_group_members_refresh(context, instance) self.trigger_instance_add_security_group_refresh(context, instance) return self.get_instance_nw_info(context, instance, networks=nets)