Skip to content

Commit

Permalink
feat: support DHCP
Browse files Browse the repository at this point in the history
  • Loading branch information
caohuilong authored and oilbeater committed Feb 25, 2022
1 parent 8393f32 commit 8e204be
Show file tree
Hide file tree
Showing 10 changed files with 427 additions and 11 deletions.
14 changes: 14 additions & 0 deletions dist/images/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,10 @@ spec:
type: number
activateGateway:
type: string
dhcpV4OptionsUUID:
type: string
dhcpV6OptionsUUID:
type: string
conditions:
type: array
items:
Expand Down Expand Up @@ -613,6 +617,16 @@ spec:
type: boolean
htbqos:
type: string
enableDHCP:
type: boolean
dhcpV4Options:
type: string
dhcpV6Options:
type: string
enableIPv6RA:
type: boolean
ipv6RAConfigs:
type: string
scope: Cluster
names:
plural: subnets
Expand Down
21 changes: 21 additions & 0 deletions docs/subnet.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,27 @@ Since kube-ovn v1.8.0, kube-ovn support using designative egress ip on node, the
- `disableGatewayCheck`: By default Kube-OVN checks Pod's network by sending ICMP request to the subnet's gateway. Set it to `true` if the subnet is in underlay mode and the physical gateway does not respond to ICMP requests.
- `disableInterConnection`: if enable cluster-interconnection, use this field to disable auto route.

## DHCP Options

OVN implements native DHCPv4 and DHCPv6 support which provides stateless replies to DHCPv4 and DHCPv6 requests.

Now kube-ovn support [DHCP feature](https://github.com/kubeovn/kube-ovn/pull/1320) too, you can enable it in the spec of subnet. It will create DHCPv4 options or DHCPv6 options, and patch the UUIDs into the status of subnet.

When a pod created, the logical switch port will associate with the DHCP options to use DHCP feature.

If you want to use DHCPv6, you may need ipv6 router advertisement too. It will send the prefix, default gateway and other infos to the DHCPv6 client.

- `enableDHCP`: Boolean, set true to enable DHCP feature for the subnet. If it's a `Dual` subnet, both DHCPv4 and DHCPv6 will be enabled. Default: false.
- `dhcpV4Options`: String, the DHCP options setting of IPv4, it works only when `enableDHCP` is true. If not set, the default configuration is: `"lease_time=3600, router=$ipv4_gateway, server_id=169.254.0.254, server_mac=$random_mac1"`.
- `dhcpV6Options`: String, the DHCP options setting of IPv6, it works only when `enableDHCP` is true. If not set, the default configuration is: `"server_id=$random_mac1"`.
- `enableIPv6RA`: Boolean, set true to enable IPv6 router advertisement. Default: false.
- `ipv6RAConfigs`: String, the ipv6_ra_configs of the logical_router_port, it works only when `enableIPv6RA` is true. If not set, the default configuration is: `"address_mode=dhcpv6_stateful, max_interval=30, min_interval=5, send_periodic=true"`.

For more information about configuration of DHCP options, please see [docs](https://www.ovn.org/support/dist-docs/ovn-nb.5.html) and [example](https://blog.oddbit.com/post/2019-12-19-ovn-and-dhcp/).

> Tips: DHCP options is very useful for the pod which implement VirtualMachines to get an ip address by DHCP, such as [KubeVirt](https://github.com/kubevirt/kubevirt) scheme will manage VM in the pod.

## Bind Pod to Subnet

By default, Pod will automatically inherit subnet from Namespace, From 1.5.1 users can bind Pod to another Subnet by manually setup the `logical_switch` annotation for a Pod.
Expand Down
23 changes: 16 additions & 7 deletions pkg/apis/kubeovn/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,13 @@ type SubnetSpec struct {
LogicalGateway bool `json:"logicalGateway"`
DisableGatewayCheck bool `json:"disableGatewayCheck"`
DisableInterConnection bool `json:"disableInterConnection"`

EnableDHCP bool `json:"enableDHCP"`
DHCPv4Options string `json:"dhcpV4Options"`
DHCPv6Options string `json:"dhcpV6Options"`

EnableIPv6RA bool `json:"enableIPv6RA"`
IPv6RAConfigs string `json:"ipv6RAConfigs"`
}

// ConditionType encodes information on the condition
Expand Down Expand Up @@ -159,13 +166,15 @@ type SubnetStatus struct {
// +patchStrategy=merge
Conditions []SubnetCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"`

AvailableIPs float64 `json:"availableIPs"`
UsingIPs float64 `json:"usingIPs"`
V4AvailableIPs float64 `json:"v4availableIPs"`
V4UsingIPs float64 `json:"v4usingIPs"`
V6AvailableIPs float64 `json:"v6availableIPs"`
V6UsingIPs float64 `json:"v6usingIPs"`
ActivateGateway string `json:"activateGateway"`
AvailableIPs float64 `json:"availableIPs"`
UsingIPs float64 `json:"usingIPs"`
V4AvailableIPs float64 `json:"v4availableIPs"`
V4UsingIPs float64 `json:"v4usingIPs"`
V6AvailableIPs float64 `json:"v6availableIPs"`
V6UsingIPs float64 `json:"v6usingIPs"`
ActivateGateway string `json:"activateGateway"`
DHCPv4OptionsUUID string `json:"dhcpV4OptionsUUID"`
DHCPv6OptionsUUID string `json:"dhcpV6OptionsUUID"`
}

// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
Expand Down
5 changes: 5 additions & 0 deletions pkg/apis/kubeovn/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions pkg/controller/gc.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,27 @@ func (c *Controller) gcLogicalSwitch() error {
}
}
}

klog.Infof("start to gc dhcp options")
dhcpOptions, err := c.ovnClient.ListDHCPOptions(c.config.EnableExternalVpc, "", "")
if err != nil {
klog.Errorf("failed to list dhcp options, %v", err)
return err
}
var uuidToDeleteList = []string{}
for _, item := range dhcpOptions {
ls := item.ExternalIds["ls"]
if !util.IsStringIn(ls, subnetNames) {
uuidToDeleteList = append(uuidToDeleteList, item.UUID)
}
}
klog.Infof("gc dhcp options %v", uuidToDeleteList)
if len(uuidToDeleteList) > 0 {
if err = c.ovnClient.DeleteDHCPOptionsByUUIDs(uuidToDeleteList); err != nil {
klog.Errorf("failed to delete dhcp options by uuids, %v", err)
return err
}
}
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/controller/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ func (c *Controller) handleAddNode(key string) error {
}

ipStr := util.GetStringIP(v4IP, v6IP)
if err := c.ovnClient.CreatePort(c.config.NodeSwitch, portName, ipStr, mac, "", "", false, "", "", false); err != nil {
if err := c.ovnClient.CreatePort(c.config.NodeSwitch, portName, ipStr, mac, "", "", false, "", "", false, false, nil); err != nil {
return err
}

Expand Down
6 changes: 5 additions & 1 deletion pkg/controller/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,11 @@ func (c *Controller) handleAddPod(key string) error {
}
}
portName := ovs.PodNameToPortName(name, namespace, podNet.ProviderName)
if err := c.ovnClient.CreatePort(subnet.Name, portName, ipStr, mac, pod.Name, pod.Namespace, portSecurity, securityGroupAnnotation, vips, podNet.AllowLiveMigration); err != nil {
dhcpOptions := &ovs.DHCPOptionsUUIDs{
DHCPv4OptionsUUID: subnet.Status.DHCPv4OptionsUUID,
DHCPv6OptionsUUID: subnet.Status.DHCPv6OptionsUUID,
}
if err := c.ovnClient.CreatePort(subnet.Name, portName, ipStr, mac, pod.Name, pod.Namespace, portSecurity, securityGroupAnnotation, vips, podNet.AllowLiveMigration, podNet.Subnet.Spec.EnableDHCP, dhcpOptions); err != nil {
c.recorder.Eventf(pod, v1.EventTypeWarning, "CreateOVNPortFailed", err.Error())
return err
}
Expand Down
41 changes: 40 additions & 1 deletion pkg/controller/subnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ func (c *Controller) enqueueUpdateSubnet(old, new interface{}) {
oldSubnet.Spec.Gateway != newSubnet.Spec.Gateway ||
!reflect.DeepEqual(oldSubnet.Spec.ExcludeIps, newSubnet.Spec.ExcludeIps) ||
!reflect.DeepEqual(oldSubnet.Spec.Vips, newSubnet.Spec.Vips) ||
oldSubnet.Spec.Vlan != newSubnet.Spec.Vlan {
oldSubnet.Spec.Vlan != newSubnet.Spec.Vlan ||
oldSubnet.Spec.EnableDHCP != newSubnet.Spec.EnableDHCP ||
oldSubnet.Spec.DHCPv4Options != newSubnet.Spec.DHCPv4Options ||
oldSubnet.Spec.DHCPv6Options != newSubnet.Spec.DHCPv6Options ||
oldSubnet.Spec.EnableIPv6RA != newSubnet.Spec.EnableIPv6RA ||
oldSubnet.Spec.IPv6RAConfigs != newSubnet.Spec.IPv6RAConfigs {
klog.V(3).Infof("enqueue update subnet %s", key)
c.addOrUpdateSubnetQueue.Add(key)
}
Expand Down Expand Up @@ -629,6 +634,35 @@ func (c *Controller) handleAddOrUpdateSubnet(key string) error {
}
}

var dhcpOptionsUUIDs *ovs.DHCPOptionsUUIDs
dhcpOptionsUUIDs, err = c.ovnClient.UpdateDHCPOptions(subnet.Name, subnet.Spec.CIDRBlock, subnet.Spec.Gateway, subnet.Spec.DHCPv4Options, subnet.Spec.DHCPv6Options, subnet.Spec.EnableDHCP)
if err != nil {
klog.Errorf("failed to update dhcp options for switch %s, %v", subnet.Name, err)
return err
}

if needRouter {
if err := c.ovnClient.UpdateRouterPortIPv6RA(subnet.Name, vpc.Status.Router, subnet.Spec.CIDRBlock, subnet.Spec.Gateway, subnet.Spec.IPv6RAConfigs, subnet.Spec.EnableIPv6RA); err != nil {
klog.Errorf("failed to update ipv6 ra configs for router port %s-%s, %v", vpc.Status.Router, subnet.Name, err)
return err
}
}

if subnet.Status.DHCPv4OptionsUUID != dhcpOptionsUUIDs.DHCPv4OptionsUUID || subnet.Status.DHCPv6OptionsUUID != dhcpOptionsUUIDs.DHCPv6OptionsUUID {
subnet.Status.DHCPv4OptionsUUID = dhcpOptionsUUIDs.DHCPv4OptionsUUID
subnet.Status.DHCPv6OptionsUUID = dhcpOptionsUUIDs.DHCPv6OptionsUUID
bytes, err := subnet.Status.Bytes()
if err != nil {
klog.Error(err)
return err
} else {
if _, err := c.config.KubeOvnClient.KubeovnV1().Subnets().Patch(context.Background(), subnet.Name, types.MergePatchType, bytes, metav1.PatchOptions{}, "status"); err != nil {
klog.Error("patch subnet %s dhcp options failed: %v", subnet.Name, err)
return err
}
}
}

if err = c.updateNodeAddressSetsForSubnet(subnet, false); err != nil {
klog.Errorf("failed to update node address sets for addition of subnet %s: %v", subnet.Name, err)
return err
Expand Down Expand Up @@ -759,6 +793,11 @@ func (c *Controller) handleDeleteLogicalSwitch(key string) (err error) {
return err
}

if err = c.ovnClient.DeleteDHCPOptions(key, kubeovnv1.ProtocolDual); err != nil {
klog.Errorf("failed to delete dhcp options of logical switch %s %v", key, err)
return err
}

if err = c.ovnClient.DeleteLogicalSwitch(key); err != nil {
klog.Errorf("failed to delete logical switch %s %v", key, err)
return err
Expand Down

0 comments on commit 8e204be

Please sign in to comment.