// down takes down an IP address for a vserver, then takes down all services // for that IP address. func (v *vserver) down(ip seesaw.IP) { ncc := v.engine.ncc if err := ncc.Dial(); err != nil { log.Fatalf("%v: failed to connect to NCC: %v", v, err) } defer ncc.Close() // If this is an anycast VIP, withdraw the BGP route. nip := ip.IP() if seesaw.IsAnycast(nip) { if v.engine.config.AnycastEnabled { log.Infof("%v: withdrawing BGP route for %v", v, ip) if err := ncc.BGPWithdrawVIP(nip); err != nil { log.Fatalf("%v: failed to withdraw VIP %v: %v", v, ip, err) } } vip := seesaw.NewVIP(nip, nil) if err := v.engine.lbInterface.DeleteVIP(vip); err != nil { log.Fatalf("%v: failed to remove VIP %v: %v", v, ip, err) } if err := v.engine.lbInterface.DeleteVserver(v.lbVservers[ip], ip.AF()); err != nil { log.Fatalf("%v: failed to delete Vserver: %v", v, err) } } // TODO(jsing): Should we delay while the BGP routes propagate? delete(v.active, ip) v.updateServices(ip) log.Infof("%v: VIP %v down", v, ip) }
// updateState updates the state of an IP for a vserver based on the state of // that IP's services. func (v *vserver) updateState(ip seesaw.IP) { // A vserver anycast IP is healthy if *all* services for that IP are healthy. // A vserver unicast IP is healthy if *any* services for that IP are healthy. var healthy bool for _, s := range v.services { if !s.ip.Equal(ip) { continue } healthy = s.healthy if !healthy && seesaw.IsAnycast(ip.IP()) { break } if healthy && !seesaw.IsAnycast(ip.IP()) { break } } if v.active[ip] == healthy { v.updateServices(ip) return } switch { case !healthy && v.active[ip]: v.down(ip) case healthy && !v.active[ip]: v.up(ip) } }
// markBackend returns a mark for the specified backend and sets up the IPVS // service entry if it does not exist. func (h *healthcheckManager) markBackend(backend seesaw.IP) uint32 { mark, ok := h.marks[backend] if ok { return mark } mark, err := h.markAlloc.get() if err != nil { log.Fatalf("Failed to get mark: %v", err) } h.marks[backend] = mark ip := net.IPv6zero if backend.AF() == seesaw.IPv4 { ip = net.IPv4zero } ipvsSvc := &ipvs.Service{ Address: ip, Protocol: ipvs.IPProto(0), Port: 0, Scheduler: "rr", FirewallMark: mark, Destinations: []*ipvs.Destination{ { Address: backend.IP(), Port: 0, Weight: 1, Flags: ipvs.DFForwardRoute, }, }, } if err := h.ncc.Dial(); err != nil { log.Fatalf("Failed to connect to NCC: %v", err) } defer h.ncc.Close() log.Infof("Adding DSR IPVS service for %s (mark %d)", backend, mark) if err := h.ncc.IPVSAddService(ipvsSvc); err != nil { log.Fatalf("Failed to add IPVS service for DSR: %v", err) } return mark }
// up brings up all healthy services for an IP address for a vserver, then // brings up the IP address. func (v *vserver) up(ip seesaw.IP) { ncc := v.engine.ncc if err := ncc.Dial(); err != nil { log.Fatalf("%v: failed to connect to NCC: %v", v, err) } defer ncc.Close() v.active[ip] = true v.updateServices(ip) // If this is an anycast VIP, start advertising a BGP route. nip := ip.IP() if seesaw.IsAnycast(nip) { // TODO(jsing): Create an LBVserver that only encapsulates // the necessary state, rather than storing a full vserver // snapshot. lbVserver := v.snapshot() lbVserver.Services = nil lbVserver.Warnings = nil if err := v.engine.lbInterface.AddVserver(lbVserver, ip.AF()); err != nil { log.Fatalf("%v: failed to add Vserver: %v", v, err) } v.lbVservers[ip] = lbVserver vip := seesaw.NewVIP(nip, nil) if err := v.engine.lbInterface.AddVIP(vip); err != nil { log.Fatalf("%v: failed to add VIP %v: %v", v, ip, err) } // TODO(angusc): Filter out anycast VIPs for non-anycast clusters further // upstream. if v.engine.config.AnycastEnabled { log.Infof("%v: advertising BGP route for %v", v, ip) if err := ncc.BGPAdvertiseVIP(nip); err != nil { log.Fatalf("%v: failed to advertise VIP %v: %v", v, ip, err) } } else { log.Warningf("%v: %v is an anycast VIP, but anycast is not enabled", v, ip) } } log.Infof("%v: VIP %v up", v, ip) }
// unmarkBackend removes the mark for a given backend and removes the IPVS // service entry if it exists. func (h *healthcheckManager) unmarkBackend(backend seesaw.IP) { mark, ok := h.marks[backend] if !ok { return } ip := net.IPv6zero if backend.AF() == seesaw.IPv4 { ip = net.IPv4zero } ipvsSvc := &ipvs.Service{ Address: ip, Protocol: ipvs.IPProto(0), Port: 0, Scheduler: "rr", FirewallMark: mark, Destinations: []*ipvs.Destination{ { Address: backend.IP(), Port: 0, Weight: 1, Flags: ipvs.DFForwardRoute, }, }, } if err := h.ncc.Dial(); err != nil { log.Fatalf("Failed to connect to NCC: %v", err) } defer h.ncc.Close() log.Infof("Removing DSR IPVS service for %s (mark %d)", backend, mark) if err := h.ncc.IPVSDeleteService(ipvsSvc); err != nil { log.Fatalf("Failed to remove DSR IPVS service: %v", err) } delete(h.marks, backend) h.markAlloc.put(mark) }