def test_delete_port_on_error_port_does_not_exist(self): core_plugin, context = mock.Mock(), mock.Mock() port_id = 'pid' core_plugin.delete_port.side_effect = exceptions.PortNotFound( port_id=port_id) with testtools.ExpectedException(exceptions.PortNotFound): with utils.delete_port_on_error(core_plugin, context, port_id): raise exceptions.PortNotFound(port_id=port_id) core_plugin.delete_port.assert_called_once_with(context, port_id, l3_port_check=False)
def get_port(self, context, _id, fields=None): try: b_port = self.core_plugin.get_port(context, _id, fields) except q_exceptions.NotFound: t_ctx = t_context.get_context_from_neutron_context(context) if self._skip_non_api_query(t_ctx): raise q_exceptions.PortNotFound(port_id=_id) t_port = self.neutron_handle.handle_get(t_ctx, 'port', _id) if not t_port: raise q_exceptions.PortNotFound(port_id=_id) self._ensure_network_subnet(context, t_port) self._adapt_port_body_for_call(t_port) self._handle_security_group(t_ctx, context, t_port) b_port = self.core_plugin.create_port(context, {'port': t_port}) return self._fields(b_port, fields)
def update_floatingip(self, context, fpid, floatingip): port_id_specified = 'port_id' in floatingip['floatingip'] if not port_id_specified: LOG.error(_LE("port_id key is not found in %s"), floatingip) raise exc.PortNotFound(port_id=None) port_id = floatingip['floatingip'].get('port_id') try: if port_id_specified and not port_id: floating = context.session.query(l3_db.FloatingIP).filter_by( id=fpid).one() self._delete_nat(context, floating) except sa.orm.exc.NoResultFound: raise exc.PortNotFound(port_id=port_id) ret = super(NECNWAL3Plugin, self).update_floatingip( context, fpid, floatingip) try: if port_id_specified and port_id: floating = context.session.query(l3_db.FloatingIP).filter_by( id=fpid).one() tenant_id = nwa_l3_db.get_tenant_id_by_router( context.session, floating['router_id'] ) nwa_tenant_id = nwa_com_utils.get_nwa_tenant_id(tenant_id) fl_data = { 'floating_ip_address': floating['floating_ip_address'], 'fixed_ip_address': floating['fixed_ip_address'], 'id': fpid, 'device_id': floating['router_id'], 'floating_network_id': floating['floating_network_id'], 'tenant_id': floating['tenant_id'], 'floating_port_id': floating['floating_port_id'] } LOG.info(_LI('setting_nat fl_data is %s'), fl_data) proxy = self._get_nwa_proxy(self, tenant_id) proxy.setting_nat( context, tenant_id=tenant_id, nwa_tenant_id=nwa_tenant_id, floating=fl_data ) except sa.orm.exc.NoResultFound: raise exc.PortNotFound(port_id=port_id) return ret
def create(self): with db_api.autonested_transaction(self.obj_context.session): try: super(SubPort, self).create() except o_db_exc.DBReferenceError as ex: if ex.key_table is None: # NOTE(ivc): 'key_table' is provided by 'oslo.db' [1] # only for a limited set of database backends (i.e. # MySQL and PostgreSQL). Other database backends # (including SQLite) would have 'key_table' set to None. # We emulate the 'key_table' support for such database # backends. # # [1] https://github.com/openstack/oslo.db/blob/3fadd5a # /oslo_db/sqlalchemy/exc_filters.py#L190-L203 if not Trunk.get_object(self.obj_context, id=self.trunk_id): ex.key_table = Trunk.db_model.__tablename__ if ex.key_table == Trunk.db_model.__tablename__: raise t_exc.TrunkNotFound(trunk_id=self.trunk_id) raise n_exc.PortNotFound(port_id=self.port_id) except base.NeutronDbObjectDuplicateEntry: raise t_exc.DuplicateSubPort( segmentation_type=self.segmentation_type, segmentation_id=self.segmentation_id, trunk_id=self.trunk_id)
def test_update_port_missing_port_on_update(self): self.plugin.get_port.return_value = { 'device_id': n_const.DEVICE_ID_RESERVED_DHCP_PORT} self.plugin.update_port.side_effect = n_exc.PortNotFound(port_id='66') self.assertIsNone(self.callbacks.update_dhcp_port( context='ctx', host='host', port_id='66', port={'port': {'network_id': 'a'}}))
def delete_port(context, id): """Delete a port. : param context: neutron api request context : param id: UUID representing the port to delete. """ LOG.info("delete_port %s for tenant %s" % (id, context.tenant_id)) port = db_api.port_find(context, id=id, scope=db_api.ONE) if not port: raise n_exc.PortNotFound(port_id=id) if 'device_id' in port: # false is weird, but ignore that LOG.info("delete_port %s for tenant %s has device %s" % (id, context.tenant_id, port['device_id'])) backend_key = port["backend_key"] mac_address = netaddr.EUI(port["mac_address"]).value ipam_driver = _get_ipam_driver(port["network"], port=port) ipam_driver.deallocate_mac_address(context, mac_address) ipam_driver.deallocate_ips_by_port( context, port, ipam_reuse_after=CONF.QUARK.ipam_reuse_after) net_driver = _get_net_driver(port["network"], port=port) base_net_driver = _get_net_driver(port["network"]) net_driver.delete_port(context, backend_key, device_id=port["device_id"], mac_address=port["mac_address"], base_net_driver=base_net_driver) with context.session.begin(): db_api.port_delete(context, port)
def _get_port(self, context, id, lazy_fields=None): try: port = model_query.get_by_id(context, models_v2.Port, id, lazy_fields=lazy_fields) except exc.NoResultFound: raise exceptions.PortNotFound(port_id=id) return port
def get_port_for_ip_address(context, ip_id, id, fields=None): """Retrieve a port. : param context: neutron api request context : param id: UUID representing the port to fetch. : param fields: a list of strings that are valid keys in a port dictionary as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. Only these fields will be returned. """ LOG.info("get_port %s for tenant %s fields %s" % (id, context.tenant_id, fields)) addr = db_api.ip_address_find(context, id=ip_id, scope=db_api.ONE) if not addr: raise q_exc.IpAddressNotFound(addr_id=ip_id) filters = {'ip_address_id': [ip_id]} results = db_api.port_find(context, id=id, fields=fields, scope=db_api.ONE, **filters) if not results: raise n_exc.PortNotFound(port_id=id) return v._make_port_for_ip_dict(addr, results)
def _get_port(context, port_id): port = db_api.port_find(context, id=port_id, scope=db_api.ONE) if not port: raise n_exc.PortNotFound(port_id=port_id) if not port.ip_addresses or len(port.ip_addresses) == 0: raise q_exc.NoAvailableFixedIpsForPort(port_id=port_id) return port
def test_update_port_missing_port_on_get(self): self.plugin.get_port.side_effect = n_exc.PortNotFound(port_id='66') self.assertIsNone( self.callbacks.update_dhcp_port(context='ctx', host='host', port_id='66', port={'port': { 'network_id': 'a' }}))
def test_delete_floatingip_deleted_port_no_notify(self): port_id = 'bee50827-bcee-4cc8-91c1-a27b0ce54222' with mock.patch.object(directory.get_plugin(), 'get_port', side_effect=n_exc.PortNotFound(port_id=port_id)): returned_obj = {'floatingip': {'port_id': port_id}} event = self.nova_notifier.create_port_changed_event( 'delete_floatingip', {}, returned_obj) self.assertIsNone(event)
def test_convert_exception_to_http_exc_multiple_same_codes(self): e = exceptions.MultipleExceptions([ exceptions.NetworkNotFound(net_id='nid'), exceptions.PortNotFound(port_id='pid') ]) conv = common.convert_exception_to_http_exc(e, base_v2.FAULT_MAP, None) self.assertIsInstance(conv, exc.HTTPNotFound) self.assertEqual( "Network nid could not be found.\nPort pid could not be found.", jsonutils.loads(conv.body)['NeutronError']['message'])
def test_remove_router_interface(self, getp): router_id = 'router-id' interface_info = {'port_id': 'router-port-id'} getp.side_effect = n_exc.PortNotFound(port_id='router-port-id') self.l3_inst.remove_router_interface(self.context, router_id, interface_info) self.l3_inst._ovn.delete_lrouter_port.assert_called_once_with( 'lrp-router-port-id', 'neutron-router-id', if_exists=True)
def test_update_routers_states_port_not_found(self): router1 = self._create_router() port = {'id': 'foo', 'device_id': router1['id']} with mock.patch.object(self.core_plugin, 'get_ports', return_value=[port]): with mock.patch.object( self.core_plugin, 'update_port', side_effect=n_exc.PortNotFound(port_id='foo')): states = {router1['id']: 'active'} self.plugin.update_routers_states( self.admin_ctx, states, self.agent1['host'])
def test_delete_resource_port_handle_port_not_found(self): mgmt_port_id = 'fake_port_id' mocked_plugin = mock.MagicMock() mock_ctx = mock.MagicMock() mocked_plugin.delete_port = mock.MagicMock( side_effect=n_exc.PortNotFound(port_id=mgmt_port_id)) with mock.patch.object(VIFHotPlugPluggingDriver, '_core_plugin') as plugin: plugin.__get__ = mock.MagicMock(return_value=mocked_plugin) vif_plugging_driver = VIFHotPlugPluggingDriver() vif_plugging_driver._delete_resource_port(mock_ctx, mgmt_port_id) self.assertEqual(1, mocked_plugin.delete_port.call_count)
def clean_by_port(self, port): """Detach port from policy and cleanup data we don't need anymore.""" port_id = port['port_id'] if port_id in self.port_policies: del self.port_policies[port_id] for qos_policy_id, port_dict in self.qos_policy_ports.items(): if port_id in port_dict: del port_dict[port_id] if not port_dict: self._clean_policy_info(qos_policy_id) return raise exceptions.PortNotFound(port_id=port['port_id'])
def test_convert_exception_to_http_exc_multiple_different_codes(self): e = exceptions.MultipleExceptions([ exceptions.NetworkInUse(net_id='nid'), exceptions.PortNotFound(port_id='pid') ]) conv = common.convert_exception_to_http_exc(e, base_v2.FAULT_MAP, None) self.assertIsInstance(conv, exc.HTTPConflict) self.assertEqual( ("HTTP 409 NetworkInUse: Unable to complete operation on network " "nid. There are one or more ports still in use on the network.\n" "HTTP 404 PortNotFound: Port pid could not be found."), jsonutils.loads(conv.body)['NeutronError']['message'])
def test_synchronize_port_not_found_in_db_no_raise(self): ctx = context.get_admin_context() with self._populate_data(ctx): # Put a port down to verify synchronization lp_uuid = list(self.fc._fake_lswitch_lport_dict)[0] lport = self.fc._fake_lswitch_lport_dict[lp_uuid] q_port_id = self._get_tag_dict(lport['tags'])['q_port_id'] lport['status'] = 'true' q_port_data = self._plugin._get_port(ctx, q_port_id) with mock.patch.object(self._plugin, '_get_port') as _get_port: _get_port.side_effect = n_exc.PortNotFound( port_id=q_port_data['id']) self._plugin._synchronizer.synchronize_port(ctx, q_port_data)
def test_update_port_unbound_agent(self): self.plugin.get_port.return_value = { 'device_id': n_const.DEVICE_ID_RESERVED_DHCP_PORT} self.plugin.update_port.side_effect = n_exc.PortNotFound(port_id='66') self.callbacks.is_agent_bound_to_network = \ _fake_is_agent_bound_to_network_false port = {'port': {'network_id': 'a'}} self.assertRaises(exceptions.DhcpPortInUse, self.callbacks.update_dhcp_port, mock.Mock(), host='host', port_id='66', port=port)
def test_remove_router_interface(self, getp): router_id = 'router-id' interface_info = {'port_id': 'router-port-id'} getp.side_effect = n_exc.PortNotFound(port_id='router-port-id') with mock.patch( 'neutron.db.l3_db.L3_NAT_dbonly_mixin.' 'remove_router_interface', return_value=interface_info): self.l3_plugin.remove_router_interface(self.context, router_id, interface_info) self.l3_plugin._ovn.delete_lrouter_port.assert_called_once_with( 'lrp-router-port-id', 'neutron-router-id', if_exists=False)
def test_remove_router_interface_with_gateway_set(self, gr, gs, gp): router_id = 'router-id' interface_info = {'port_id': 'router-port-id', 'subnet_id': 'subnet-id'} gr.return_value = self.fake_router_with_ext_gw gs.return_value = self.fake_subnet gp.side_effect = n_exc.PortNotFound(port_id='router-port-id') self.l3_inst.remove_router_interface( self.context, router_id, interface_info) self.l3_inst._ovn.delete_lrouter_port.assert_called_once_with( 'lrp-router-port-id', 'neutron-router-id', if_exists=True) self.l3_inst._ovn.delete_nat_rule_in_lrouter.assert_called_once_with( 'neutron-router-id', logical_ip='10.0.0.0/24', external_ip='192.168.1.1', type='snat')
def diagnose_port(context, id, fields): if not context.is_admin: raise n_exc.NotAuthorized() if id == "*": return { 'ports': [ _diag_port(context, port, fields) for port in db_api.port_find(context).all() ] } db_port = db_api.port_find(context, id=id, scope=db_api.ONE) if not db_port: raise n_exc.PortNotFound(port_id=id) port = _diag_port(context, db_port, fields) return {'ports': port}
def create(self): with db_api.autonested_transaction(self.obj_context.session): sub_ports = [] if self.obj_attr_is_set('sub_ports'): sub_ports = self.sub_ports try: super(Trunk, self).create() except o_db_exc.DBReferenceError: raise n_exc.PortNotFound(port_id=self.port_id) if sub_ports: for sub_port in sub_ports: sub_port.trunk_id = self.id sub_port.create() self.sub_ports.append(sub_port) self.obj_reset_changes(['sub_ports'])
def create(self): with self.db_context_writer(self.obj_context): sub_ports = [] if self.obj_attr_is_set('sub_ports'): sub_ports = self.sub_ports try: super(Trunk, self).create() except o_db_exc.DBReferenceError: raise n_exc.PortNotFound(port_id=self.port_id) if sub_ports: for sub_port in sub_ports: sub_port.trunk_id = self.id sub_port.create() self.sub_ports.append(sub_port) self.obj_reset_changes(['sub_ports'])
def _get_port(self, context, id): try: port = self._get_by_id(context, models_v2.Port, id) except exc.NoResultFound: raise n_exc.PortNotFound(port_id=id) return port
def test_update_device_up_notify_not_sent_with_port_not_found(self): self.plugin._get_port.side_effect = (exceptions.PortNotFound( port_id='foo_port_id')) notify = self._test_update_device_up() self.assertFalse(notify.call_count)
def update_ip_address(context, id, ip_address): """Due to NCP-1592 ensure that address_type cannot change after update.""" LOG.info("update_ip_address %s for tenant %s" % (id, context.tenant_id)) ports = [] if 'ip_address' not in ip_address: raise n_exc.BadRequest(resource="ip_addresses", msg="Invalid request body.") with context.session.begin(): address = db_api.ip_address_find(context, id=id, scope=db_api.ONE) if not address: raise q_exc.IpAddressNotFound(addr_id=id) iptype = address.address_type if iptype == ip_types.FIXED and not CONF.QUARK.ipaddr_allow_fixed_ip: raise n_exc.BadRequest( resource="ip_addresses", msg="Fixed ips cannot be updated using this interface.") reset = ip_address['ip_address'].get('reset_allocation_time', False) if reset and address['deallocated'] == 1: if context.is_admin: LOG.info("IP's deallocated time being manually reset") address['deallocated_at'] = _get_deallocated_override() else: msg = "Modification of reset_allocation_time requires admin" raise webob.exc.HTTPForbidden(detail=msg) port_ids = ip_address['ip_address'].get('port_ids', None) if port_ids is not None and not port_ids: raise n_exc.BadRequest( resource="ip_addresses", msg="Cannot be updated with empty port_id list") if iptype == ip_types.SHARED: has_owner = address.has_any_shared_owner() if port_ids: if iptype == ip_types.FIXED and len(port_ids) > 1: raise n_exc.BadRequest( resource="ip_addresses", msg="Fixed ips cannot be updated with more than one port.") _raise_if_shared_and_enabled(ip_address, address) ports = db_api.port_find(context, tenant_id=context.tenant_id, id=port_ids, scope=db_api.ALL) # NOTE(name): could be considered inefficient because we're # converting to a list to check length. Maybe revisit if len(ports) != len(port_ids): raise n_exc.PortNotFound(port_id=port_ids) validate_and_fetch_segment(ports, address["network_id"]) validate_port_ip_quotas(context, address.network_id, ports) if iptype == ip_types.SHARED and has_owner: for assoc in address.associations: pid = assoc.port_id if pid not in port_ids and 'none' != assoc.service: raise q_exc.PortRequiresDisassociation() LOG.info("Updating IP address, %s, to only be used by the" "following ports: %s" % (address.address_readable, [p.id for p in ports])) new_address = db_api.update_port_associations_for_ip( context, ports, address) elif iptype == ip_types.SHARED and has_owner: raise q_exc.PortRequiresDisassociation() else: ipam_driver.deallocate_ip_address(context, address) return v._make_ip_dict(address) return v._make_ip_dict(new_address)
def _update_flip(context, flip_id, ip_type, requested_ports): """Update a flip based IPAddress :param context: neutron api request context. :param flip_id: id of the flip or scip :param ip_type: ip_types.FLOATING | ip_types.SCALING :param requested_ports: dictionary of the structure: {"port_id": "<id of port>", "fixed_ip": "<fixed ip address>"} :return: quark.models.IPAddress """ # This list will hold flips that require notifications. # Using sets to avoid dups, if any. notifications = {billing.IP_ASSOC: set(), billing.IP_DISASSOC: set()} context.session.begin() try: flip = db_api.floating_ip_find(context, id=flip_id, scope=db_api.ONE) if not flip: if ip_type == ip_types.SCALING: raise q_exc.ScalingIpNotFound(id=flip_id) raise q_exc.FloatingIpNotFound(id=flip_id) current_ports = flip.ports # Determine what ports are being removed, being added, and remain req_port_ids = [ request_port.get('port_id') for request_port in requested_ports ] curr_port_ids = [curr_port.id for curr_port in current_ports] added_port_ids = [ port_id for port_id in req_port_ids if port_id and port_id not in curr_port_ids ] removed_port_ids = [ port_id for port_id in curr_port_ids if port_id not in req_port_ids ] remaining_port_ids = set(curr_port_ids) - set(removed_port_ids) # Validations just for floating ip types if (ip_type == ip_types.FLOATING and curr_port_ids and curr_port_ids == req_port_ids): d = dict(flip_id=flip_id, port_id=curr_port_ids[0]) raise q_exc.PortAlreadyAssociatedToFloatingIp(**d) if (ip_type == ip_types.FLOATING and not curr_port_ids and not req_port_ids): raise q_exc.FloatingIpUpdateNoPortIdSupplied() # Validate that GW IP is not in use on the NW. flip_subnet = v._make_subnet_dict(flip.subnet) for added_port_id in added_port_ids: port = _get_port(context, added_port_id) nw = port.network nw_ports = v._make_ports_list(nw.ports) fixed_ips = [ ip.get('ip_address') for p in nw_ports for ip in p.get('fixed_ips') ] gw_ip = flip_subnet.get('gateway_ip') if gw_ip in fixed_ips: port_with_gateway_ip = None for port in nw_ports: for ip in port.get('fixed_ips'): if gw_ip in ip.get('ip_address'): port_with_gateway_ip = port break port_id = port_with_gateway_ip.get('id') network_id = port_with_gateway_ip.get('network_id') raise q_exc.FixedIpAllocatedToGatewayIp(port_id=port_id, network_id=network_id) port_fixed_ips = {} # Keep the ports and fixed ips that have not changed for port_id in remaining_port_ids: port = db_api.port_find(context, id=port_id, scope=db_api.ONE) fixed_ip = _get_flip_fixed_ip_by_port_id(flip, port_id) port_fixed_ips[port_id] = {'port': port, 'fixed_ip': fixed_ip} # Disassociate the ports and fixed ips from the flip that were # associated to the flip but are not anymore for port_id in removed_port_ids: port = db_api.port_find(context, id=port_id, scope=db_api.ONE) flip = db_api.port_disassociate_ip(context, [port], flip) notifications[billing.IP_DISASSOC].add(flip) fixed_ip = _get_flip_fixed_ip_by_port_id(flip, port_id) if fixed_ip: flip = db_api.floating_ip_disassociate_fixed_ip( context, flip, fixed_ip) # Validate the new ports with the flip and associate the new ports # and fixed ips with the flip for port_id in added_port_ids: port = db_api.port_find(context, id=port_id, scope=db_api.ONE) if not port: raise n_exc.PortNotFound(port_id=port_id) if any(ip for ip in port.ip_addresses if (ip.get('address_type') == ip_types.FLOATING)): raise q_exc.PortAlreadyContainsFloatingIp(port_id=port_id) if any(ip for ip in port.ip_addresses if (ip.get('address_type') == ip_types.SCALING)): raise q_exc.PortAlreadyContainsScalingIp(port_id=port_id) fixed_ip = _get_next_available_fixed_ip(port) LOG.info('new fixed ip: %s' % fixed_ip) if not fixed_ip: raise q_exc.NoAvailableFixedIpsForPort(port_id=port_id) port_fixed_ips[port_id] = {'port': port, 'fixed_ip': fixed_ip} flip = db_api.port_associate_ip(context, [port], flip, [port_id]) notifications[billing.IP_ASSOC].add(flip) flip = db_api.floating_ip_associate_fixed_ip( context, flip, fixed_ip) flip_driver = registry.DRIVER_REGISTRY.get_driver() # If there are not any remaining ports and no new ones are being added, # remove the floating ip from unicorn if not remaining_port_ids and not added_port_ids: flip_driver.remove_floating_ip(flip) # If new ports are being added but there previously was not any ports, # then register a new floating ip with the driver because it is # assumed it does not exist elif added_port_ids and not curr_port_ids: flip_driver.register_floating_ip(flip, port_fixed_ips) else: flip_driver.update_floating_ip(flip, port_fixed_ips) context.session.commit() except Exception: context.session.rollback() raise # Send notifications for possible associate/disassociate events for notif_type, flip_set in notifications.iteritems(): for flip in flip_set: billing.notify(context, notif_type, flip) # NOTE(blogan): ORM does not seem to update the model to the real state # of the database, so I'm doing an explicit refresh for now. context.session.refresh(flip) return flip
def update_port(context, id, port): """Update values of a port. : param context: neutron api request context : param id: UUID representing the port to update. : param port: dictionary with keys indicating fields to update. valid keys are those that have a value of True for 'allow_put' as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. """ LOG.info("update_port %s for tenant %s" % (id, context.tenant_id)) port_db = db_api.port_find(context, id=id, scope=db_api.ONE) if not port_db: raise n_exc.PortNotFound(port_id=id) port_dict = port["port"] fixed_ips = port_dict.pop("fixed_ips", None) admin_only = [ "mac_address", "device_owner", "bridge", "admin_state_up", "device_id" ] always_filter = ["network_id", "backend_key", "network_plugin"] utils.filter_body(context, port_dict, admin_only=admin_only, always_filter=always_filter) # Pre-check the requested fixed_ips before making too many db trips. # Note that this is the only check we need, since this call replaces # the entirety of the IP addresses document if fixed_ips are provided. if fixed_ips: quota.QUOTAS.limit_check(context, context.tenant_id, fixed_ips_per_port=len(fixed_ips)) new_security_groups = utils.pop_param(port_dict, "security_groups") if new_security_groups is not None: if (Capabilities.TENANT_NETWORK_SG not in CONF.QUARK.environment_capabilities): if not STRATEGY.is_provider_network(port_db["network_id"]): raise q_exc.TenantNetworkSecurityGroupRulesNotEnabled() if new_security_groups is not None and not port_db["device_id"]: raise q_exc.SecurityGroupsRequireDevice() group_ids, security_group_mods = _make_security_group_list( context, new_security_groups) quota.QUOTAS.limit_check(context, context.tenant_id, security_groups_per_port=len(group_ids)) if fixed_ips is not None: # NOTE(mdietz): we want full control over IPAM since # we're allocating by subnet instead of # network. ipam_driver = ipam.IPAM_REGISTRY.get_strategy( ipam.QuarkIpamANY.get_name()) addresses, subnet_ids = [], [] ip_addresses = {} for fixed_ip in fixed_ips: subnet_id = fixed_ip.get("subnet_id") ip_address = fixed_ip.get("ip_address") if not (subnet_id or ip_address): raise n_exc.BadRequest(resource="fixed_ips", msg="subnet_id or ip_address required") if ip_address and not subnet_id: raise n_exc.BadRequest( resource="fixed_ips", msg="subnet_id required for ip_address allocation") if subnet_id and ip_address: ip_netaddr = None try: ip_netaddr = netaddr.IPAddress(ip_address).ipv6() except netaddr.AddrFormatError: raise n_exc.InvalidInput( error_message="Invalid format provided for ip_address") ip_addresses[ip_netaddr] = subnet_id else: subnet_ids.append(subnet_id) port_ips = set([ netaddr.IPAddress(int(a["address"])) for a in port_db["ip_addresses"] ]) new_ips = set([a for a in ip_addresses.keys()]) ips_to_allocate = list(new_ips - port_ips) ips_to_deallocate = list(port_ips - new_ips) for ip in ips_to_allocate: if ip in ip_addresses: # NOTE: Fix for RM10187 - we were losing the list of IPs if # more than one IP was to be allocated. Track an # aggregate list instead, and add it to the running total # after each allocate allocated = [] ipam_driver.allocate_ip_address(context, allocated, port_db["network_id"], port_db["id"], reuse_after=None, ip_addresses=[ip], subnets=[ip_addresses[ip]]) addresses.extend(allocated) for ip in ips_to_deallocate: ipam_driver.deallocate_ips_by_port(context, port_db, ip_address=ip) for subnet_id in subnet_ids: ipam_driver.allocate_ip_address( context, addresses, port_db["network_id"], port_db["id"], reuse_after=CONF.QUARK.ipam_reuse_after, subnets=[subnet_id]) # Need to return all existing addresses and the new ones if addresses: port_dict["addresses"] = port_db["ip_addresses"] port_dict["addresses"].extend(addresses) # NOTE(morgabra) Updating network_plugin on port objects is explicitly # disallowed in the api, so we use whatever exists in the db. net_driver = _get_net_driver(port_db.network, port=port_db) base_net_driver = _get_net_driver(port_db.network) # TODO(anyone): What do we want to have happen here if this fails? Is it # ok to continue to keep the IPs but fail to apply security # groups? Is there a clean way to have a multi-status? Since # we're in a beta-y status, I'm going to let this sit for # a future patch where we have time to solve it well. kwargs = {} if new_security_groups is not None: # TODO(anyone): this is kind of silly (when testing), because it will # modify the incoming dict. Probably should be a copy or # something. kwargs["security_groups"] = security_group_mods net_driver.update_port(context, port_id=port_db["backend_key"], mac_address=port_db["mac_address"], device_id=port_db["device_id"], base_net_driver=base_net_driver, **kwargs) port_dict["security_groups"] = security_group_mods with context.session.begin(): port = db_api.port_update(context, port_db, **port_dict) # NOTE(mdietz): fix for issue 112, we wanted the IPs to be in # allocated_at order, so get a fresh object every time if port_db in context.session: context.session.expunge(port_db) port_db = db_api.port_find(context, id=id, scope=db_api.ONE) return v._make_port_dict(port_db)
def test_prevent_l3_port_deletion_port_not_found(self, gp): # port not found doesn't prevent gp.return_value.get_port.side_effect = n_exc.PortNotFound(port_id='1') self.db.prevent_l3_port_deletion(None, None)