def create_subnet(context, subnet): """Create a subnet. Create a subnet which represents a range of IP addresses that can be allocated to devices : param context: neutron api request context : param subnet: dictionary describing the subnet, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_subnet for tenant %s" % context.tenant_id) net_id = subnet["subnet"]["network_id"] net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) sub_attrs = subnet["subnet"] _validate_subnet_cidr(context, net_id, sub_attrs["cidr"]) cidr = netaddr.IPNetwork(sub_attrs["cidr"]) gateway_ip = utils.pop_param(sub_attrs, "gateway_ip", str(cidr[1])) dns_ips = utils.pop_param(sub_attrs, "dns_nameservers", []) host_routes = utils.pop_param(sub_attrs, "host_routes", []) allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", []) sub_attrs["network"] = net new_subnet = db_api.subnet_create(context, **sub_attrs) default_route = None for route in host_routes: netaddr_route = netaddr.IPNetwork(route["destination"]) if netaddr_route.value == routes.DEFAULT_ROUTE.value: default_route = route gateway_ip = default_route["nexthop"] new_subnet["routes"].append(db_api.route_create( context, cidr=route["destination"], gateway=route["nexthop"])) if default_route is None: new_subnet["routes"].append(db_api.route_create( context, cidr=str(routes.DEFAULT_ROUTE), gateway=gateway_ip)) for dns_ip in dns_ips: new_subnet["dns_nameservers"].append(db_api.dns_create( context, ip=netaddr.IPAddress(dns_ip))) if allocation_pools: exclude = netaddr.IPSet([cidr]) for p in allocation_pools: x = netaddr.IPSet(netaddr.IPRange(p["start"], p["end"])) exclude = exclude - x new_subnet["ip_policy"] = db_api.ip_policy_create(context, exclude=exclude) subnet_dict = v._make_subnet_dict(new_subnet, default_route=routes.DEFAULT_ROUTE) subnet_dict["gateway_ip"] = gateway_ip return subnet_dict
def _adapt_provider_nets(context, network): # TODO(mdietz) going to ignore all the boundary and network # type checking for now. attrs = network["network"] net_type = utils.pop_param(attrs, pnet.NETWORK_TYPE) phys_net = utils.pop_param(attrs, pnet.PHYSICAL_NETWORK) seg_id = utils.pop_param(attrs, pnet.SEGMENTATION_ID) return net_type, phys_net, seg_id
def update_network(context, id, network): """Update values of a network. : param context: neutron api request context : param id: UUID representing the network to update. : param network: 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_network %s for tenant %s" % (id, context.tenant_id)) with context.session.begin(): net = db_api.network_find(context, id=id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=id) net_dict = network["network"] utils.pop_param(net_dict, "network_plugin") if not context.is_admin and "ipam_strategy" in net_dict: utils.pop_param(net_dict, "ipam_strategy") net = db_api.network_update(context, net, **net_dict) return v._make_network_dict(net)
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") net_id = port_attrs["network_id"] device_id = port_attrs["device_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) if not STRATEGY.is_parent_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check( context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"]) net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"]) group_ids, security_groups = v.make_security_group_list( context, port["port"].pop("security_groups", None)) addresses = [] mac = None backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac): if fixed_ips: for fixed_ip in fixed_ips: subnet_id = fixed_ip.get("subnet_id") ip_address = fixed_ip.get("ip_address") if not (subnet_id and ip_address): raise exceptions.BadRequest( resource="fixed_ips", msg="subnet_id and ip_address required") ipam_driver.allocate_ip_address( context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, ip_address=ip_address, subnets=[subnet_id], mac_address=mac) else: ipam_driver.allocate_ip_address( context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac) @cmd_mgr.undo def _allocate_ips_undo(addr): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address(context, address) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address): mac = ipam_driver.allocate_mac_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address) return mac @cmd_mgr.undo def _allocate_mac_undo(mac): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address(context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id): backend_port = net_driver.create_port(context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port): LOG.info("Rolling back backend port...") try: net_driver.delete_port(context, backend_port["uuid"]) except Exception: LOG.exception( "Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create( context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception( "Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)
def create_subnet(context, subnet): """Create a subnet. Create a subnet which represents a range of IP addresses that can be allocated to devices : param context: neutron api request context : param subnet: dictionary describing the subnet, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_subnet for tenant %s" % context.tenant_id) net_id = subnet["subnet"]["network_id"] with context.session.begin(): net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) sub_attrs = subnet["subnet"] _validate_subnet_cidr(context, net_id, sub_attrs["cidr"]) cidr = netaddr.IPNetwork(sub_attrs["cidr"]) gateway_ip = utils.pop_param(sub_attrs, "gateway_ip", str(cidr[1])) dns_ips = utils.pop_param(sub_attrs, "dns_nameservers", []) host_routes = utils.pop_param(sub_attrs, "host_routes", []) allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", None) sub_attrs["network"] = net new_subnet = db_api.subnet_create(context, **sub_attrs) default_route = None for route in host_routes: netaddr_route = netaddr.IPNetwork(route["destination"]) if netaddr_route.value == routes.DEFAULT_ROUTE.value: default_route = route gateway_ip = default_route["nexthop"] new_subnet["routes"].append(db_api.route_create( context, cidr=route["destination"], gateway=route["nexthop"])) if default_route is None: new_subnet["routes"].append(db_api.route_create( context, cidr=str(routes.DEFAULT_ROUTE), gateway=gateway_ip)) for dns_ip in dns_ips: new_subnet["dns_nameservers"].append(db_api.dns_create( context, ip=netaddr.IPAddress(dns_ip))) if isinstance(allocation_pools, list): ranges = [] cidrset = netaddr.IPSet([netaddr.IPNetwork(new_subnet["cidr"])]) for p in allocation_pools: cidrset -= netaddr.IPSet(netaddr.IPRange(p["start"], p["end"])) non_allocation_pools = v._pools_from_cidr(cidrset) for p in non_allocation_pools: r = netaddr.IPRange(p["start"], p["end"]) ranges.append(dict( length=len(r), offset=int(r[0]) - int(cidr[0]))) new_subnet["ip_policy"] = db_api.ip_policy_create(context, exclude=ranges) subnet_dict = v._make_subnet_dict(new_subnet, default_route=routes.DEFAULT_ROUTE) subnet_dict["gateway_ip"] = gateway_ip notifier_api.notify(context, notifier_api.publisher_id("network"), "ip_block.create", notifier_api.CONF.default_notification_level, dict(tenant_id=subnet_dict["tenant_id"], ip_block_id=subnet_dict["id"], created_at=new_subnet["created_at"])) return subnet_dict
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up", "use_forbidden_mac_range", "network_plugin", "instance_node_id"] utils.filter_body(context, port_attrs, admin_only=admin_only) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) use_forbidden_mac_range = utils.pop_param(port_attrs, "use_forbidden_mac_range", False) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") if "device_id" not in port_attrs: port_attrs['device_id'] = "" device_id = port_attrs['device_id'] # NOTE(morgabra) This should be instance.node from nova, only needed # for ironic_driver. if "instance_node_id" not in port_attrs: port_attrs['instance_node_id'] = "" instance_node_id = port_attrs['instance_node_id'] net_id = port_attrs["network_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context=context, limit=None, sorts=['id'], marker=None, page_reverse=False, fields=None, id=net_id, scope=db_api.ONE) if not net: raise n_exc.NetworkNotFound(net_id=net_id) _raise_if_unauthorized(context, net) # NOTE (Perkins): If a device_id is given, try to prevent multiple ports # from being created for a device already attached to the network if device_id: existing_ports = db_api.port_find(context, network_id=net_id, device_id=device_id, scope=db_api.ONE) if existing_ports: raise n_exc.BadRequest( resource="port", msg="This device is already connected to the " "requested network via another port") # Try to fail early on quotas and save ourselves some db overhead if fixed_ips: quota.QUOTAS.limit_check(context, context.tenant_id, fixed_ips_per_port=len(fixed_ips)) if not STRATEGY.is_provider_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check( context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) network_plugin = utils.pop_param(port_attrs, "network_plugin") if not network_plugin: network_plugin = net["network_plugin"] port_attrs["network_plugin"] = network_plugin ipam_driver = _get_ipam_driver(net, port=port_attrs) net_driver = _get_net_driver(net, port=port_attrs) # NOTE(morgabra) It's possible that we select a driver different than # the one specified by the network. However, we still might need to use # this for some operations, so we also fetch it and pass it along to # the backend driver we are actually using. base_net_driver = _get_net_driver(net) # TODO(anyone): security groups are not currently supported on port create. # Please see JIRA:NCP-801 security_groups = utils.pop_param(port_attrs, "security_groups") if security_groups is not None: raise q_exc.SecurityGroupsNotImplemented() group_ids, security_groups = _make_security_group_list(context, security_groups) quota.QUOTAS.limit_check(context, context.tenant_id, security_groups_per_port=len(group_ids)) addresses = [] backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac, **kwargs): if fixed_ips: if (STRATEGY.is_provider_network(net_id) and not context.is_admin): raise n_exc.NotAuthorized() ips, subnets = split_and_validate_requested_subnets(context, net_id, segment_id, fixed_ips) kwargs["ip_addresses"] = ips kwargs["subnets"] = subnets ipam_driver.allocate_ip_address( context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac, **kwargs) @cmd_mgr.undo def _allocate_ips_undo(addr, **kwargs): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address(context, address, **kwargs) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=False, **kwargs): mac = ipam_driver.allocate_mac_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address, use_forbidden_mac_range=use_forbidden_mac_range, **kwargs) return mac @cmd_mgr.undo def _allocate_mac_undo(mac, **kwargs): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address(context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id, **kwargs): backend_port = net_driver.create_port( context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id, instance_node_id=instance_node_id, mac_address=mac, addresses=addresses, base_net_driver=base_net_driver) _filter_backend_port(backend_port) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port, **kwargs): LOG.info("Rolling back backend port...") try: backend_port_uuid = None if backend_port: backend_port_uuid = backend_port.get("uuid") net_driver.delete_port(context, backend_port_uuid) except Exception: LOG.exception( "Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac, **kwargs): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create( context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port, **kwargs): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception( "Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=use_forbidden_mac_range) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)
def create_subnet(context, subnet): """Create a subnet. Create a subnet which represents a range of IP addresses that can be allocated to devices : param context: neutron api request context : param subnet: dictionary describing the subnet, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_subnet for tenant %s" % context.tenant_id) net_id = subnet["subnet"]["network_id"] with context.session.begin(): net = db_api.network_find(context, None, None, None, False, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) sub_attrs = subnet["subnet"] always_pop = ["enable_dhcp", "ip_version", "first_ip", "last_ip", "_cidr"] admin_only = ["segment_id", "do_not_use", "created_at", "next_auto_assign_ip"] utils.filter_body(context, sub_attrs, admin_only, always_pop) _validate_subnet_cidr(context, net_id, sub_attrs["cidr"]) cidr = netaddr.IPNetwork(sub_attrs["cidr"]) err_vals = {'cidr': sub_attrs["cidr"], 'network_id': net_id} err = _("Requested subnet with cidr: %(cidr)s for " "network: %(network_id)s. Prefix is too small, must be a " "larger subnet. A prefix less than /%(prefix)s is required.") if cidr.version == 6 and cidr.prefixlen > 64: err_vals["prefix"] = 65 err_msg = err % err_vals raise exceptions.InvalidInput(error_message=err_msg) elif cidr.version == 4 and cidr.prefixlen > 30: err_vals["prefix"] = 31 err_msg = err % err_vals raise exceptions.InvalidInput(error_message=err_msg) # Enforce subnet quotas net_subnets = get_subnets(context, filters=dict(network_id=net_id)) if not context.is_admin: v4_count, v6_count = 0, 0 for subnet in net_subnets: if netaddr.IPNetwork(subnet['cidr']).version == 6: v6_count += 1 else: v4_count += 1 if cidr.version == 6: tenant_quota_v6 = context.session.query(qdv.Quota).filter_by( tenant_id=context.tenant_id, resource='v6_subnets_per_network').first() if tenant_quota_v6 != -1: quota.QUOTAS.limit_check( context, context.tenant_id, v6_subnets_per_network=v6_count + 1) else: tenant_quota_v4 = context.session.query(qdv.Quota).filter_by( tenant_id=context.tenant_id, resource='v4_subnets_per_network').first() if tenant_quota_v4 != -1: quota.QUOTAS.limit_check( context, context.tenant_id, v4_subnets_per_network=v4_count + 1) # See RM981. The default behavior of setting a gateway unless # explicitly asked to not is no longer desirable. gateway_ip = utils.pop_param(sub_attrs, "gateway_ip") dns_ips = utils.pop_param(sub_attrs, "dns_nameservers", []) host_routes = utils.pop_param(sub_attrs, "host_routes", []) allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", None) sub_attrs["network"] = net new_subnet = db_api.subnet_create(context, **sub_attrs) cidrs = [] alloc_pools = allocation_pool.AllocationPools(sub_attrs["cidr"], allocation_pools) if isinstance(allocation_pools, list): cidrs = alloc_pools.get_policy_cidrs() quota.QUOTAS.limit_check( context, context.tenant_id, alloc_pools_per_subnet=len(alloc_pools)) ip_policies.ensure_default_policy(cidrs, [new_subnet]) new_subnet["ip_policy"] = db_api.ip_policy_create(context, exclude=cidrs) quota.QUOTAS.limit_check(context, context.tenant_id, routes_per_subnet=len(host_routes)) default_route = None for route in host_routes: netaddr_route = netaddr.IPNetwork(route["destination"]) if netaddr_route.value == routes.DEFAULT_ROUTE.value: if default_route: raise q_exc.DuplicateRouteConflict( subnet_id=new_subnet["id"]) default_route = route gateway_ip = default_route["nexthop"] alloc_pools.validate_gateway_excluded(gateway_ip) new_subnet["routes"].append(db_api.route_create( context, cidr=route["destination"], gateway=route["nexthop"])) quota.QUOTAS.limit_check(context, context.tenant_id, dns_nameservers_per_subnet=len(dns_ips)) for dns_ip in dns_ips: new_subnet["dns_nameservers"].append(db_api.dns_create( context, ip=netaddr.IPAddress(dns_ip))) # if the gateway_ip is IN the cidr for the subnet and NOT excluded by # policies, we should raise a 409 conflict if gateway_ip and default_route is None: alloc_pools.validate_gateway_excluded(gateway_ip) new_subnet["routes"].append(db_api.route_create( context, cidr=str(routes.DEFAULT_ROUTE), gateway=gateway_ip)) subnet_dict = v._make_subnet_dict(new_subnet) subnet_dict["gateway_ip"] = gateway_ip n_rpc.get_notifier("network").info( context, "ip_block.create", dict(tenant_id=subnet_dict["tenant_id"], ip_block_id=subnet_dict["id"], created_at=new_subnet["created_at"])) return subnet_dict
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up", "use_forbidden_mac_range"] utils.filter_body(context, port_attrs, admin_only=admin_only) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) use_forbidden_mac_range = utils.pop_param(port_attrs, "use_forbidden_mac_range", False) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") if "device_id" not in port_attrs: port_attrs['device_id'] = "" device_id = port_attrs['device_id'] net_id = port_attrs["network_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context, None, None, None, False, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) _raise_if_unauthorized(context.tenant_id, net) # NOTE (Perkins): If a device_id is given, try to prevent multiple ports # from being created for a device already attached to the network if device_id: existing_ports = db_api.port_find(context, network_id=net_id, device_id=device_id, scope=db_api.ONE) if existing_ports: raise exceptions.BadRequest( resource="port", msg="This device is already connected to the " "requested network via another port") # Try to fail early on quotas and save ourselves some db overhead if fixed_ips: quota.QUOTAS.limit_check(context, context.tenant_id, fixed_ips_per_port=len(fixed_ips)) if not STRATEGY.is_parent_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check( context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"]) net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"]) # TODO(anyone): security groups are not currently supported on port create, # nor on isolated networks today. Please see RM8615 security_groups = utils.pop_param(port_attrs, "security_groups") if security_groups is not None: raise q_exc.SecurityGroupsNotImplemented() group_ids, security_groups = _make_security_group_list(context, security_groups) quota.QUOTAS.limit_check(context, context.tenant_id, security_groups_per_port=len(group_ids)) addresses = [] backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac): fixed_ip_kwargs = {} if fixed_ips: if STRATEGY.is_parent_network(net_id) and not context.is_admin: raise exceptions.NotAuthorized() ips, subnets = split_and_validate_requested_subnets(context, net_id, segment_id, fixed_ips) fixed_ip_kwargs["ip_addresses"] = ips fixed_ip_kwargs["subnets"] = subnets ipam_driver.allocate_ip_address( context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac, **fixed_ip_kwargs) @cmd_mgr.undo def _allocate_ips_undo(addr): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address(context, address) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=False): mac = ipam_driver.allocate_mac_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address, use_forbidden_mac_range=use_forbidden_mac_range) return mac @cmd_mgr.undo def _allocate_mac_undo(mac): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address(context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id): backend_port = net_driver.create_port(context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port): LOG.info("Rolling back backend port...") try: net_driver.delete_port(context, backend_port["uuid"]) except Exception: LOG.exception( "Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create( context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception( "Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=use_forbidden_mac_range) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)
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 exceptions.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 exceptions.BadRequest( resource="fixed_ips", msg="subnet_id or ip_address required") if ip_address and not subnet_id: raise exceptions.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 exceptions.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: 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 create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] admin_only = [ "mac_address", "device_owner", "bridge", "admin_state_up", "use_forbidden_mac_range", "network_plugin", "instance_node_id" ] utils.filter_body(context, port_attrs, admin_only=admin_only) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) use_forbidden_mac_range = utils.pop_param(port_attrs, "use_forbidden_mac_range", False) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") if "device_id" not in port_attrs: port_attrs['device_id'] = "" device_id = port_attrs['device_id'] # NOTE(morgabra) This should be instance.node from nova, only needed # for ironic_driver. if "instance_node_id" not in port_attrs: port_attrs['instance_node_id'] = "" instance_node_id = port_attrs['instance_node_id'] net_id = port_attrs["network_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context, None, None, None, False, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) _raise_if_unauthorized(context, net) # NOTE (Perkins): If a device_id is given, try to prevent multiple ports # from being created for a device already attached to the network if device_id: existing_ports = db_api.port_find(context, network_id=net_id, device_id=device_id, scope=db_api.ONE) if existing_ports: raise exceptions.BadRequest( resource="port", msg="This device is already connected to the " "requested network via another port") # Try to fail early on quotas and save ourselves some db overhead if fixed_ips: quota.QUOTAS.limit_check(context, context.tenant_id, fixed_ips_per_port=len(fixed_ips)) if not STRATEGY.is_provider_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check(context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) network_plugin = utils.pop_param(port_attrs, "network_plugin") if not network_plugin: network_plugin = net["network_plugin"] port_attrs["network_plugin"] = network_plugin ipam_driver = _get_ipam_driver(net, port=port_attrs) net_driver = _get_net_driver(net, port=port_attrs) # NOTE(morgabra) It's possible that we select a driver different than # the one specified by the network. However, we still might need to use # this for some operations, so we also fetch it and pass it along to # the backend driver we are actually using. base_net_driver = _get_net_driver(net) # TODO(anyone): security groups are not currently supported on port create. # Please see JIRA:NCP-801 security_groups = utils.pop_param(port_attrs, "security_groups") if security_groups is not None: raise q_exc.SecurityGroupsNotImplemented() group_ids, security_groups = _make_security_group_list( context, security_groups) quota.QUOTAS.limit_check(context, context.tenant_id, security_groups_per_port=len(group_ids)) addresses = [] backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac): fixed_ip_kwargs = {} if fixed_ips: if (STRATEGY.is_provider_network(net_id) and not context.is_admin): raise exceptions.NotAuthorized() ips, subnets = split_and_validate_requested_subnets( context, net_id, segment_id, fixed_ips) fixed_ip_kwargs["ip_addresses"] = ips fixed_ip_kwargs["subnets"] = subnets ipam_driver.allocate_ip_address(context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac, **fixed_ip_kwargs) @cmd_mgr.undo def _allocate_ips_undo(addr): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address(context, address) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=False): mac = ipam_driver.allocate_mac_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address, use_forbidden_mac_range=use_forbidden_mac_range) return mac @cmd_mgr.undo def _allocate_mac_undo(mac): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address( context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id): backend_port = net_driver.create_port( context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id, instance_node_id=instance_node_id, mac_address=mac, addresses=addresses, base_net_driver=base_net_driver) _filter_backend_port(backend_port) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port): LOG.info("Rolling back backend port...") try: backend_port_uuid = None if backend_port: backend_port_uuid = backend_port.get("uuid") net_driver.delete_port(context, backend_port_uuid) except Exception: LOG.exception("Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create(context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception("Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address, use_forbidden_mac_range=use_forbidden_mac_range) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] admin_only = ["mac_address", "device_owner", "bridge", "admin_state_up"] utils.filter_body(context, port_attrs, admin_only=admin_only) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") if "device_id" not in port_attrs: port_attrs['device_id'] = "" device_id = port_attrs['device_id'] net_id = port_attrs["network_id"] port_id = uuidutils.generate_uuid() net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) _raise_if_unauthorized(context.tenant_id, net) # NOTE (Perkins): If a device_id is given, try to prevent multiple ports # from being created for a device already attached to the network if device_id: existing_ports = db_api.port_find(context, network_id=net_id, device_id=device_id, scope=db_api.ONE) if existing_ports: raise exceptions.BadRequest( resource="port", msg="This device is already connected to the " "requested network via another port") if not STRATEGY.is_parent_network(net_id): # We don't honor segmented networks when they aren't "shared" segment_id = None port_count = db_api.port_count_all(context, network_id=[net_id], tenant_id=[context.tenant_id]) quota.QUOTAS.limit_check(context, context.tenant_id, ports_per_network=port_count + 1) else: if not segment_id: raise q_exc.AmbiguousNetworkId(net_id=net_id) ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"]) net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"]) # TODO(anyone): security groups are not currently supported on port create, # nor on isolated networks today. Please see RM8615 security_groups = utils.pop_param(port_attrs, "security_groups") if security_groups: raise q_exc.SecurityGroupsNotImplemented() group_ids, security_groups = _make_security_group_list( context, security_groups) quota.QUOTAS.limit_check(context, context.tenant_id, security_groups_per_port=len(group_ids)) addresses = [] mac = None backend_port = None with utils.CommandManager().execute() as cmd_mgr: @cmd_mgr.do def _allocate_ips(fixed_ips, net, port_id, segment_id, mac): ips = [] subnets = [] if fixed_ips: for fixed_ip in fixed_ips: subnet_id = fixed_ip.get("subnet_id") ip_address = fixed_ip.get("ip_address") if not (subnet_id and ip_address): raise exceptions.BadRequest( resource="fixed_ips", msg="subnet_id and ip_address required") ips.append(ip_address) subnets.append(subnet_id) ipam_driver.allocate_ip_address(context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, ip_addresses=ips, subnets=subnets, mac_address=mac) else: ipam_driver.allocate_ip_address(context, addresses, net["id"], port_id, CONF.QUARK.ipam_reuse_after, segment_id=segment_id, mac_address=mac) @cmd_mgr.undo def _allocate_ips_undo(addr): LOG.info("Rolling back IP addresses...") if addresses: for address in addresses: try: with context.session.begin(): ipam_driver.deallocate_ip_address(context, address) except Exception: LOG.exception("Couldn't release IP %s" % address) @cmd_mgr.do def _allocate_mac(net, port_id, mac_address): mac = ipam_driver.allocate_mac_address(context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address) return mac @cmd_mgr.undo def _allocate_mac_undo(mac): LOG.info("Rolling back MAC address...") if mac: try: with context.session.begin(): ipam_driver.deallocate_mac_address( context, mac["address"]) except Exception: LOG.exception("Couldn't release MAC %s" % mac) @cmd_mgr.do def _allocate_backend_port(mac, addresses, net, port_id): backend_port = net_driver.create_port(context, net["id"], port_id=port_id, security_groups=group_ids, device_id=device_id) return backend_port @cmd_mgr.undo def _allocate_back_port_undo(backend_port): LOG.info("Rolling back backend port...") try: net_driver.delete_port(context, backend_port["uuid"]) except Exception: LOG.exception("Couldn't rollback backend port %s" % backend_port) @cmd_mgr.do def _allocate_db_port(port_attrs, backend_port, addresses, mac): port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) with context.session.begin(): new_port = db_api.port_create(context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return new_port @cmd_mgr.undo def _allocate_db_port_undo(new_port): LOG.info("Rolling back database port...") if not new_port: return try: with context.session.begin(): db_api.port_delete(context, new_port) except Exception: LOG.exception("Couldn't rollback db port %s" % backend_port) # addresses, mac, backend_port, new_port mac = _allocate_mac(net, port_id, mac_address) _allocate_ips(fixed_ips, net, port_id, segment_id, mac) backend_port = _allocate_backend_port(mac, addresses, net, port_id) new_port = _allocate_db_port(port_attrs, backend_port, addresses, mac) return v._make_port_dict(new_port)
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 exceptions.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"] utils.filter_body(context, port_dict, admin_only=admin_only, always_filter=always_filter) # TODO(anyone): security groups are not currently supported on port create, # nor on isolated networks today. Please see RM8615 security_groups = utils.pop_param(port_dict, "security_groups") if security_groups: if not STRATEGY.is_parent_network(port_db["network_id"]): raise q_exc.TenantNetworkSecurityGroupsNotImplemented() group_ids, security_groups = _make_security_group_list( context, 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 exceptions.BadRequest( resource="fixed_ips", msg="subnet_id or ip_address required") if ip_address and not subnet_id: raise exceptions.BadRequest( resource="fixed_ips", msg="subnet_id required for ip_address allocation") if subnet_id and ip_address: ip_netaddr = netaddr.IPAddress(ip_address).ipv6() 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: ipam_driver.allocate_ip_address(context, addresses, port_db["network_id"], port_db["id"], reuse_after=None, ip_addresses=[ip], subnets=[ip_addresses[ip]]) 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) net_driver = registry.DRIVER_REGISTRY.get_driver( port_db.network["network_plugin"]) # 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. net_driver.update_port(context, port_id=port_db["backend_key"], mac_address=port_db["mac_address"], security_groups=security_groups) port_dict["security_groups"] = security_groups 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 create_subnet(context, subnet): """Create a subnet. Create a subnet which represents a range of IP addresses that can be allocated to devices : param context: neutron api request context : param subnet: dictionary describing the subnet, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_subnet for tenant %s" % context.tenant_id) net_id = subnet["subnet"]["network_id"] with context.session.begin(): net = db_api.network_find(context, None, None, None, False, id=net_id, scope=db_api.ONE) if not net: raise n_exc.NetworkNotFound(net_id=net_id) sub_attrs = subnet["subnet"] always_pop = [ "enable_dhcp", "ip_version", "first_ip", "last_ip", "_cidr" ] admin_only = [ "segment_id", "do_not_use", "created_at", "next_auto_assign_ip" ] utils.filter_body(context, sub_attrs, admin_only, always_pop) _validate_subnet_cidr(context, net_id, sub_attrs["cidr"]) cidr = netaddr.IPNetwork(sub_attrs["cidr"]) err_vals = {'cidr': sub_attrs["cidr"], 'network_id': net_id} err = _("Requested subnet with cidr: %(cidr)s for " "network: %(network_id)s. Prefix is too small, must be a " "larger subnet. A prefix less than /%(prefix)s is required.") if cidr.version == 6 and cidr.prefixlen > 64: err_vals["prefix"] = 65 err_msg = err % err_vals raise n_exc.InvalidInput(error_message=err_msg) elif cidr.version == 4 and cidr.prefixlen > 30: err_vals["prefix"] = 31 err_msg = err % err_vals raise n_exc.InvalidInput(error_message=err_msg) # Enforce subnet quotas net_subnets = get_subnets(context, filters=dict(network_id=net_id)) if not context.is_admin: v4_count, v6_count = 0, 0 for subnet in net_subnets: if netaddr.IPNetwork(subnet['cidr']).version == 6: v6_count += 1 else: v4_count += 1 if cidr.version == 6: tenant_quota_v6 = context.session.query(qdv.Quota).filter_by( tenant_id=context.tenant_id, resource='v6_subnets_per_network').first() if tenant_quota_v6 != -1: quota.QUOTAS.limit_check(context, context.tenant_id, v6_subnets_per_network=v6_count + 1) else: tenant_quota_v4 = context.session.query(qdv.Quota).filter_by( tenant_id=context.tenant_id, resource='v4_subnets_per_network').first() if tenant_quota_v4 != -1: quota.QUOTAS.limit_check(context, context.tenant_id, v4_subnets_per_network=v4_count + 1) # See RM981. The default behavior of setting a gateway unless # explicitly asked to not is no longer desirable. gateway_ip = utils.pop_param(sub_attrs, "gateway_ip") dns_ips = utils.pop_param(sub_attrs, "dns_nameservers", []) host_routes = utils.pop_param(sub_attrs, "host_routes", []) allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", None) sub_attrs["network"] = net new_subnet = db_api.subnet_create(context, **sub_attrs) cidrs = [] alloc_pools = allocation_pool.AllocationPools(sub_attrs["cidr"], allocation_pools) if isinstance(allocation_pools, list): cidrs = alloc_pools.get_policy_cidrs() quota.QUOTAS.limit_check(context, context.tenant_id, alloc_pools_per_subnet=len(alloc_pools)) ip_policies.ensure_default_policy(cidrs, [new_subnet]) new_subnet["ip_policy"] = db_api.ip_policy_create(context, exclude=cidrs) quota.QUOTAS.limit_check(context, context.tenant_id, routes_per_subnet=len(host_routes)) default_route = None for route in host_routes: netaddr_route = netaddr.IPNetwork(route["destination"]) if netaddr_route.value == routes.DEFAULT_ROUTE.value: if default_route: raise q_exc.DuplicateRouteConflict( subnet_id=new_subnet["id"]) default_route = route gateway_ip = default_route["nexthop"] alloc_pools.validate_gateway_excluded(gateway_ip) new_subnet["routes"].append( db_api.route_create(context, cidr=route["destination"], gateway=route["nexthop"])) quota.QUOTAS.limit_check(context, context.tenant_id, dns_nameservers_per_subnet=len(dns_ips)) for dns_ip in dns_ips: new_subnet["dns_nameservers"].append( db_api.dns_create(context, ip=netaddr.IPAddress(dns_ip))) # if the gateway_ip is IN the cidr for the subnet and NOT excluded by # policies, we should raise a 409 conflict if gateway_ip and default_route is None: alloc_pools.validate_gateway_excluded(gateway_ip) new_subnet["routes"].append( db_api.route_create(context, cidr=str(routes.DEFAULT_ROUTE), gateway=gateway_ip)) subnet_dict = v._make_subnet_dict(new_subnet) subnet_dict["gateway_ip"] = gateway_ip n_rpc.get_notifier("network").info( context, "ip_block.create", dict(tenant_id=subnet_dict["tenant_id"], ip_block_id=subnet_dict["id"], created_at=new_subnet["created_at"])) return subnet_dict
def update_subnet(context, id, subnet): """Update values of a subnet. : param context: neutron api request context : param id: UUID representing the subnet to update. : param subnet: 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_subnet %s for tenant %s" % (id, context.tenant_id)) with context.session.begin(): subnet_db = db_api.subnet_find(context, None, None, None, False, id=id, scope=db_api.ONE) if not subnet_db: raise n_exc.SubnetNotFound(subnet_id=id) s = subnet["subnet"] always_pop = [ "_cidr", "cidr", "first_ip", "last_ip", "ip_version", "segment_id", "network_id" ] admin_only = [ "do_not_use", "created_at", "tenant_id", "next_auto_assign_ip", "enable_dhcp" ] utils.filter_body(context, s, admin_only, always_pop) dns_ips = utils.pop_param(s, "dns_nameservers", []) host_routes = utils.pop_param(s, "host_routes", []) gateway_ip = utils.pop_param(s, "gateway_ip", None) allocation_pools = utils.pop_param(s, "allocation_pools", None) if not CONF.QUARK.allow_allocation_pool_update: if allocation_pools: raise n_exc.BadRequest( resource="subnets", msg="Allocation pools cannot be updated.") alloc_pools = allocation_pool.AllocationPools( subnet_db["cidr"], policies=models.IPPolicy.get_ip_policy_cidrs(subnet_db)) else: alloc_pools = allocation_pool.AllocationPools( subnet_db["cidr"], allocation_pools) quota.QUOTAS.limit_check(context, context.tenant_id, alloc_pools_per_subnet=len(alloc_pools)) if gateway_ip: alloc_pools.validate_gateway_excluded(gateway_ip) default_route = None for route in host_routes: netaddr_route = netaddr.IPNetwork(route["destination"]) if netaddr_route.value == routes.DEFAULT_ROUTE.value: default_route = route break if default_route is None: route_model = db_api.route_find(context, cidr=str(routes.DEFAULT_ROUTE), subnet_id=id, scope=db_api.ONE) if route_model: db_api.route_update(context, route_model, gateway=gateway_ip) else: db_api.route_create(context, cidr=str(routes.DEFAULT_ROUTE), gateway=gateway_ip, subnet_id=id) if dns_ips: subnet_db["dns_nameservers"] = [] quota.QUOTAS.limit_check(context, context.tenant_id, dns_nameservers_per_subnet=len(dns_ips)) for dns_ip in dns_ips: subnet_db["dns_nameservers"].append( db_api.dns_create(context, ip=netaddr.IPAddress(dns_ip))) if host_routes: subnet_db["routes"] = [] quota.QUOTAS.limit_check(context, context.tenant_id, routes_per_subnet=len(host_routes)) for route in host_routes: subnet_db["routes"].append( db_api.route_create(context, cidr=route["destination"], gateway=route["nexthop"])) if CONF.QUARK.allow_allocation_pool_update: if isinstance(allocation_pools, list): cidrs = alloc_pools.get_policy_cidrs() ip_policies.ensure_default_policy(cidrs, [subnet_db]) subnet_db["ip_policy"] = db_api.ip_policy_update( context, subnet_db["ip_policy"], exclude=cidrs) # invalidate the cache db_api.subnet_update_set_alloc_pool_cache(context, subnet_db) subnet = db_api.subnet_update(context, subnet_db, **s) return v._make_subnet_dict(subnet)
def create_network(context, network): """Create a network. Create a network which represents an L2 network segment which can have a set of subnets and ports associated with it. : param context: neutron api request context : param network: dictionary describing the network, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_network for tenant %s" % context.tenant_id) with context.session.begin(): net_attrs = network["network"] subs = net_attrs.pop("subnets", []) # Enforce subnet quotas if not context.is_admin: if len(subs) > 0: v4_count, v6_count = 0, 0 for s in subs: version = netaddr.IPNetwork(s['subnet']['cidr']).version if version == 6: v6_count += 1 else: v4_count += 1 if v4_count > 0: tenant_q_v4 = context.session.query(qdb.Quota).filter_by( tenant_id=context.tenant_id, resource='v4_subnets_per_network').first() if tenant_q_v4 != -1: quota.QUOTAS.limit_check( context, context.tenant_id, v4_subnets_per_network=v4_count) if v6_count > 0: tenant_q_v6 = context.session.query(qdb.Quota).filter_by( tenant_id=context.tenant_id, resource='v6_subnets_per_network').first() if tenant_q_v6 != -1: quota.QUOTAS.limit_check( context, context.tenant_id, v6_subnets_per_network=v6_count) # Generate a uuid that we're going to hand to the backend and db net_uuid = utils.pop_param(net_attrs, "id", None) net_type = None if net_uuid and context.is_admin: net = db_api.network_find(context, id=net_uuid, scope=db_api.ONE) net_type = utils.pop_param(net_attrs, "network_plugin", None) if net: raise q_exc.NetworkAlreadyExists(id=net_uuid) else: net_uuid = uuidutils.generate_uuid() # TODO(mdietz) this will be the first component registry hook, but # lets make it work first pnet_type, phys_net, seg_id = _adapt_provider_nets(context, network) ipam_strategy = utils.pop_param(net_attrs, "ipam_strategy", None) if not ipam_strategy or not context.is_admin: ipam_strategy = CONF.QUARK.default_ipam_strategy if not ipam.IPAM_REGISTRY.is_valid_strategy(ipam_strategy): raise q_exc.InvalidIpamStrategy(strat=ipam_strategy) net_attrs["ipam_strategy"] = ipam_strategy # NOTE(mdietz) I think ideally we would create the providernet # elsewhere as a separate driver step that could be # kept in a plugin and completely removed if desired. We could # have a pre-callback/observer on the netdriver create_network # that gathers any additional parameters from the network dict default_net_type = net_type or CONF.QUARK.default_network_type net_driver = registry.DRIVER_REGISTRY.get_driver(default_net_type) net_driver.create_network(context, net_attrs["name"], network_id=net_uuid, phys_type=pnet_type, phys_net=phys_net, segment_id=seg_id) net_attrs["id"] = net_uuid net_attrs["tenant_id"] = context.tenant_id net_attrs["network_plugin"] = default_net_type new_net = db_api.network_create(context, **net_attrs) new_subnets = [] for sub in subs: sub["subnet"]["network_id"] = new_net["id"] sub["subnet"]["tenant_id"] = context.tenant_id s = db_api.subnet_create(context, **sub["subnet"]) new_subnets.append(s) new_net["subnets"] = new_subnets # if not security_groups.get_security_groups( # context, # filters={"id": security_groups.DEFAULT_SG_UUID}): # security_groups._create_default_security_group(context) return v._make_network_dict(new_net)
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 exceptions.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"] 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)) # TODO(anyone): security groups are not currently supported on port create, # nor on isolated networks today. Please see RM8615 new_security_groups = utils.pop_param(port_dict, "security_groups") if new_security_groups is not None: if not STRATEGY.is_parent_network(port_db["network_id"]): raise q_exc.TenantNetworkSecurityGroupsNotImplemented() 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 exceptions.BadRequest( resource="fixed_ips", msg="subnet_id or ip_address required") if ip_address and not subnet_id: raise exceptions.BadRequest( resource="fixed_ips", msg="subnet_id required for ip_address allocation") if subnet_id and ip_address: ip_netaddr = netaddr.IPAddress(ip_address).ipv6() 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) net_driver = registry.DRIVER_REGISTRY.get_driver( port_db.network["network_plugin"]) # 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: 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"], **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 create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") net_id = port_attrs["network_id"] addresses = [] port_id = uuidutils.generate_uuid() net = db_api.network_find(context, id=net_id, shared=True, segment_id=segment_id, scope=db_api.ONE) if not net: # Maybe it's a tenant network net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) quota.QUOTAS.limit_check( context, context.tenant_id, ports_per_network=len(net.get('ports', [])) + 1) if fixed_ips: for fixed_ip in fixed_ips: subnet_id = fixed_ip.get("subnet_id") ip_address = fixed_ip.get("ip_address") if not (subnet_id and ip_address): raise exceptions.BadRequest( resource="fixed_ips", msg="subnet_id and ip_address required") addresses.append(ipam_driver.allocate_ip_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, ip_address=ip_address)) else: addresses.append(ipam_driver.allocate_ip_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after)) group_ids, security_groups = v.make_security_group_list( context, port["port"].pop("security_groups", None)) mac = ipam_driver.allocate_mac_address(context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address) mac_address_string = str(netaddr.EUI(mac['address'], dialect=netaddr.mac_unix)) address_pairs = [{'mac_address': mac_address_string, 'ip_address': address.get('address_readable', '')} for address in addresses] backend_port = net_driver.create_port(context, net["id"], port_id=port_id, security_groups=group_ids, allowed_pairs=address_pairs) port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups new_port = db_api.port_create( context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs) return v._make_port_dict(new_port)
def update_subnet(context, id, subnet): """Update values of a subnet. : param context: neutron api request context : param id: UUID representing the subnet to update. : param subnet: 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_subnet %s for tenant %s" % (id, context.tenant_id)) with context.session.begin(): subnet_db = db_api.subnet_find(context, None, None, None, False, id=id, scope=db_api.ONE) if not subnet_db: raise exceptions.SubnetNotFound(id=id) s = subnet["subnet"] always_pop = ["_cidr", "cidr", "first_ip", "last_ip", "ip_version", "segment_id", "network_id"] admin_only = ["do_not_use", "created_at", "tenant_id", "next_auto_assign_ip", "enable_dhcp"] utils.filter_body(context, s, admin_only, always_pop) dns_ips = utils.pop_param(s, "dns_nameservers", []) host_routes = utils.pop_param(s, "host_routes", []) gateway_ip = utils.pop_param(s, "gateway_ip", None) allocation_pools = utils.pop_param(s, "allocation_pools", None) if not CONF.QUARK.allow_allocation_pool_update: if allocation_pools: raise exceptions.BadRequest( resource="subnets", msg="Allocation pools cannot be updated.") alloc_pools = allocation_pool.AllocationPools( subnet_db["cidr"], policies=models.IPPolicy.get_ip_policy_cidrs(subnet_db)) else: alloc_pools = allocation_pool.AllocationPools(subnet_db["cidr"], allocation_pools) quota.QUOTAS.limit_check( context, context.tenant_id, alloc_pools_per_subnet=len(alloc_pools)) if gateway_ip: alloc_pools.validate_gateway_excluded(gateway_ip) default_route = None for route in host_routes: netaddr_route = netaddr.IPNetwork(route["destination"]) if netaddr_route.value == routes.DEFAULT_ROUTE.value: default_route = route break if default_route is None: route_model = db_api.route_find( context, cidr=str(routes.DEFAULT_ROUTE), subnet_id=id, scope=db_api.ONE) if route_model: db_api.route_update(context, route_model, gateway=gateway_ip) else: db_api.route_create(context, cidr=str(routes.DEFAULT_ROUTE), gateway=gateway_ip, subnet_id=id) if dns_ips: subnet_db["dns_nameservers"] = [] quota.QUOTAS.limit_check(context, context.tenant_id, dns_nameservers_per_subnet=len(dns_ips)) for dns_ip in dns_ips: subnet_db["dns_nameservers"].append(db_api.dns_create( context, ip=netaddr.IPAddress(dns_ip))) if host_routes: subnet_db["routes"] = [] quota.QUOTAS.limit_check(context, context.tenant_id, routes_per_subnet=len(host_routes)) for route in host_routes: subnet_db["routes"].append(db_api.route_create( context, cidr=route["destination"], gateway=route["nexthop"])) if CONF.QUARK.allow_allocation_pool_update: if isinstance(allocation_pools, list): cidrs = alloc_pools.get_policy_cidrs() ip_policies.ensure_default_policy(cidrs, [subnet_db]) subnet_db["ip_policy"] = db_api.ip_policy_update( context, subnet_db["ip_policy"], exclude=cidrs) # invalidate the cache db_api.subnet_update_set_alloc_pool_cache(context, subnet_db) subnet = db_api.subnet_update(context, subnet_db, **s) return v._make_subnet_dict(subnet)
def create_port(context, port): """Create a port Create a port which is a connection point of a device (e.g., a VM NIC) to attach to a L2 Neutron network. : param context: neutron api request context : param port: dictionary describing the port, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_port for tenant %s" % context.tenant_id) port_attrs = port["port"] mac_address = utils.pop_param(port_attrs, "mac_address", None) segment_id = utils.pop_param(port_attrs, "segment_id") fixed_ips = utils.pop_param(port_attrs, "fixed_ips") net_id = port_attrs["network_id"] addresses = [] with context.session.begin(): port_id = uuidutils.generate_uuid() net = db_api.network_find(context, id=net_id, segment_id=segment_id, scope=db_api.ONE) if not net: # Maybe it's a tenant network net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) if not STRATEGY.is_parent_network(net_id): quota.QUOTAS.limit_check(context, context.tenant_id, ports_per_network=len(net.get("ports", [])) + 1) ipam_driver = ipam.IPAM_REGISTRY.get_strategy(net["ipam_strategy"]) if fixed_ips: for fixed_ip in fixed_ips: subnet_id = fixed_ip.get("subnet_id") ip_address = fixed_ip.get("ip_address") if not (subnet_id and ip_address): raise exceptions.BadRequest(resource="fixed_ips", msg="subnet_id and ip_address required") addresses.extend( ipam_driver.allocate_ip_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, ip_address=ip_address ) ) else: addresses.extend(ipam_driver.allocate_ip_address(context, net["id"], port_id, CONF.QUARK.ipam_reuse_after)) group_ids, security_groups = v.make_security_group_list(context, port["port"].pop("security_groups", None)) mac = ipam_driver.allocate_mac_address( context, net["id"], port_id, CONF.QUARK.ipam_reuse_after, mac_address=mac_address ) mac_address_string = str(netaddr.EUI(mac["address"], dialect=netaddr.mac_unix)) address_pairs = [ {"mac_address": mac_address_string, "ip_address": address.get("address_readable", "")} for address in addresses ] net_driver = registry.DRIVER_REGISTRY.get_driver(net["network_plugin"]) backend_port = net_driver.create_port( context, net["id"], port_id=port_id, security_groups=group_ids, allowed_pairs=address_pairs ) port_attrs["network_id"] = net["id"] port_attrs["id"] = port_id port_attrs["security_groups"] = security_groups LOG.info("Including extra plugin attrs: %s" % backend_port) port_attrs.update(backend_port) new_port = db_api.port_create( context, addresses=addresses, mac_address=mac["address"], backend_key=backend_port["uuid"], **port_attrs ) # Include any driver specific bits return v._make_port_dict(new_port)
def create_subnet(context, subnet): """Create a subnet. Create a subnet which represents a range of IP addresses that can be allocated to devices : param context: neutron api request context : param subnet: dictionary describing the subnet, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_subnet for tenant %s" % context.tenant_id) net_id = subnet["subnet"]["network_id"] with context.session.begin(): net = db_api.network_find(context, id=net_id, scope=db_api.ONE) if not net: raise exceptions.NetworkNotFound(net_id=net_id) sub_attrs = subnet["subnet"] _validate_subnet_cidr(context, net_id, sub_attrs["cidr"]) cidr = netaddr.IPNetwork(sub_attrs["cidr"]) err_vals = {'cidr': sub_attrs["cidr"], 'network_id': net_id} err = _("Requested subnet with cidr: %(cidr)s for " "network: %(network_id)s. Prefix is too small, must be a " "larger subnet. A prefix less than /%(prefix)s is required.") if cidr.version == 6 and cidr.prefixlen > 64: err_vals["prefix"] = 65 err_msg = err % err_vals raise exceptions.InvalidInput(error_message=err_msg) elif cidr.version == 4 and cidr.prefixlen > 30: err_vals["prefix"] = 31 err_msg = err % err_vals raise exceptions.InvalidInput(error_message=err_msg) gateway_ip = utils.pop_param(sub_attrs, "gateway_ip", str(cidr[1])) dns_ips = utils.pop_param(sub_attrs, "dns_nameservers", []) host_routes = utils.pop_param(sub_attrs, "host_routes", []) allocation_pools = utils.pop_param(sub_attrs, "allocation_pools", None) if not context.is_admin and "segment_id" in sub_attrs: sub_attrs.pop("segment_id") sub_attrs["network"] = net new_subnet = db_api.subnet_create(context, **sub_attrs) default_route = None for route in host_routes: netaddr_route = netaddr.IPNetwork(route["destination"]) if netaddr_route.value == routes.DEFAULT_ROUTE.value: if default_route: raise q_exc.DuplicateRouteConflict( subnet_id=new_subnet["id"]) default_route = route gateway_ip = default_route["nexthop"] new_subnet["routes"].append(db_api.route_create( context, cidr=route["destination"], gateway=route["nexthop"])) if gateway_ip and default_route is None: new_subnet["routes"].append(db_api.route_create( context, cidr=str(routes.DEFAULT_ROUTE), gateway=gateway_ip)) for dns_ip in dns_ips: new_subnet["dns_nameservers"].append(db_api.dns_create( context, ip=netaddr.IPAddress(dns_ip))) if isinstance(allocation_pools, list) and allocation_pools: subnet_net = netaddr.IPNetwork(new_subnet["cidr"]) cidrset = netaddr.IPSet( netaddr.IPRange( netaddr.IPAddress(subnet_net.first), netaddr.IPAddress(subnet_net.last)).cidrs()) for p in allocation_pools: start = netaddr.IPAddress(p["start"]) end = netaddr.IPAddress(p["end"]) cidrset -= netaddr.IPSet(netaddr.IPRange( netaddr.IPAddress(start), netaddr.IPAddress(end)).cidrs()) default_cidrset = models.IPPolicy.get_ip_policy_cidrs(new_subnet) cidrset.update(default_cidrset) cidrs = [str(x.cidr) for x in cidrset.iter_cidrs()] new_subnet["ip_policy"] = db_api.ip_policy_create(context, exclude=cidrs) subnet_dict = v._make_subnet_dict(new_subnet) subnet_dict["gateway_ip"] = gateway_ip notifier_api.notify(context, notifier_api.publisher_id("network"), "ip_block.create", notifier_api.CONF.default_notification_level, dict(tenant_id=subnet_dict["tenant_id"], ip_block_id=subnet_dict["id"], created_at=new_subnet["created_at"])) return subnet_dict
def create_network(context, network): """Create a network. Create a network which represents an L2 network segment which can have a set of subnets and ports associated with it. : param context: neutron api request context : param network: dictionary describing the network, with keys as listed in the RESOURCE_ATTRIBUTE_MAP object in neutron/api/v2/attributes.py. All keys will be populated. """ LOG.info("create_network for tenant %s" % context.tenant_id) with context.session.begin(): net_attrs = network["network"] subs = net_attrs.pop("subnets", []) # Enforce subnet quotas if not context.is_admin: if len(subs) > 0: v4_count, v6_count = 0, 0 for s in subs: version = netaddr.IPNetwork(s['subnet']['cidr']).version if version == 6: v6_count += 1 else: v4_count += 1 if v4_count > 0: tenant_q_v4 = context.session.query(qdv.Quota).filter_by( tenant_id=context.tenant_id, resource='v4_subnets_per_network').first() if tenant_q_v4 != -1: quota.QUOTAS.limit_check( context, context.tenant_id, v4_subnets_per_network=v4_count) if v6_count > 0: tenant_q_v6 = context.session.query(qdv.Quota).filter_by( tenant_id=context.tenant_id, resource='v6_subnets_per_network').first() if tenant_q_v6 != -1: quota.QUOTAS.limit_check( context, context.tenant_id, v6_subnets_per_network=v6_count) # Generate a uuid that we're going to hand to the backend and db net_uuid = utils.pop_param(net_attrs, "id", None) net_type = None if net_uuid and context.is_admin: net = db_api.network_find(context, None, None, None, False, id=net_uuid, scope=db_api.ONE) net_type = utils.pop_param(net_attrs, "network_plugin", None) if net: raise q_exc.NetworkAlreadyExists(id=net_uuid) else: net_uuid = uuidutils.generate_uuid() # TODO(mdietz) this will be the first component registry hook, but # lets make it work first pnet_type, phys_net, seg_id = _adapt_provider_nets(context, network) ipam_strategy = utils.pop_param(net_attrs, "ipam_strategy", None) if not ipam_strategy or not context.is_admin: ipam_strategy = CONF.QUARK.default_ipam_strategy if not ipam.IPAM_REGISTRY.is_valid_strategy(ipam_strategy): raise q_exc.InvalidIpamStrategy(strat=ipam_strategy) net_attrs["ipam_strategy"] = ipam_strategy # NOTE(mdietz) I think ideally we would create the providernet # elsewhere as a separate driver step that could be # kept in a plugin and completely removed if desired. We could # have a pre-callback/observer on the netdriver create_network # that gathers any additional parameters from the network dict default_net_type = net_type or CONF.QUARK.default_network_type net_driver = registry.DRIVER_REGISTRY.get_driver(default_net_type) net_driver.create_network(context, net_attrs["name"], network_id=net_uuid, phys_type=pnet_type, phys_net=phys_net, segment_id=seg_id) net_attrs["id"] = net_uuid net_attrs["tenant_id"] = context.tenant_id net_attrs["network_plugin"] = default_net_type new_net = db_api.network_create(context, **net_attrs) new_subnets = [] for sub in subs: sub["subnet"]["network_id"] = new_net["id"] sub["subnet"]["tenant_id"] = context.tenant_id s = db_api.subnet_create(context, **sub["subnet"]) new_subnets.append(s) new_net["subnets"] = new_subnets # if not security_groups.get_security_groups( # context, # filters={"id": security_groups.DEFAULT_SG_UUID}): # security_groups._create_default_security_group(context) return v._make_network_dict(new_net)