Compare commits
3 commits
032201d631
...
1ec1d44c1a
| Author | SHA1 | Date | |
|---|---|---|---|
|
1ec1d44c1a |
|||
|
f90514a5dc |
|||
|
30bb4107a1 |
9 changed files with 304 additions and 28 deletions
|
|
@ -171,32 +171,56 @@ func TestGenerateConfig_ContainsDhcpRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateConfig_OneHostEntryPerIP(t *testing.T) {
|
func TestGenerateConfig_OneEntryPerIP(t *testing.T) {
|
||||||
// /29 = réseau + broadcast + 6 hôtes → 8 adresses
|
// /29 = 8 adresses. Les entrées ne vont plus dans le fichier dnsmasq mais
|
||||||
conf := newConf(t, "10.0.0.0/29")
|
// dans la map retournée, que StoreDHCPEntries écrit en base pour GetMACForIP.
|
||||||
path, _, _ := GenerateConfig(conf)
|
_, entries, err := GenerateConfig(newConf(t, "10.0.0.0/29"))
|
||||||
content, _ := os.ReadFile(path)
|
if err != nil {
|
||||||
|
t.Fatalf("GenerateConfig : %v", err)
|
||||||
|
}
|
||||||
|
if len(entries) != 8 {
|
||||||
|
t.Errorf("attendu 8 entrées ip->mac, obtenu %d", len(entries))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lines := strings.Split(string(content), "\n")
|
func TestGenerateConfig_NoPreGeneratedHosts(t *testing.T) {
|
||||||
count := 0
|
// Une entrée dhcp-host pré-générée fait rejeter celle du dhcp-hostsdir
|
||||||
for _, l := range lines {
|
// (« duplicate dhcp-host IP address »), sans erreur : la VM reçoit alors
|
||||||
if strings.HasPrefix(l, "dhcp-host=") {
|
// les options non taggées. Vérifié sur dnsmasq 2.90.
|
||||||
count++
|
content := confLines(t, newConf(t, "10.0.0.0/29"))
|
||||||
|
if strings.Contains(content, "dhcp-host=") {
|
||||||
|
t.Errorf("aucune entrée dhcp-host ne doit être pré-générée :\n%s", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateConfig_PointsToDirs(t *testing.T) {
|
||||||
|
conf := newConf(t, "10.0.0.0/29")
|
||||||
|
content := confLines(t, conf)
|
||||||
|
|
||||||
|
for _, want := range []string{
|
||||||
|
"dhcp-hostsdir=" + HostsDir(conf.ConfDir, conf.Name),
|
||||||
|
"dhcp-optsdir=" + OptsDir(conf.ConfDir, conf.Name),
|
||||||
|
} {
|
||||||
|
if !strings.Contains(content, want) {
|
||||||
|
t.Errorf("%q absent :\n%s", want, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// /29 contient 8 adresses (0 à 7)
|
for _, dir := range []string{HostsDir(conf.ConfDir, conf.Name), OptsDir(conf.ConfDir, conf.Name)} {
|
||||||
if count != 8 {
|
if fi, err := os.Stat(dir); err != nil || !fi.IsDir() {
|
||||||
t.Errorf("attendu 8 entrées dhcp-host, obtenu %d", count)
|
t.Errorf("répertoire %q non créé : %v", dir, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateConfig_MACPrefix(t *testing.T) {
|
func TestGenerateConfig_MACPrefix(t *testing.T) {
|
||||||
conf := newConf(t, "10.0.0.0/30") // 4 adresses
|
_, entries, err := GenerateConfig(newConf(t, "10.0.0.0/30"))
|
||||||
path, _, _ := GenerateConfig(conf)
|
if err != nil {
|
||||||
content, _ := os.ReadFile(path)
|
t.Fatalf("GenerateConfig : %v", err)
|
||||||
|
}
|
||||||
if !strings.Contains(string(content), "00:22:33:") {
|
for ip, mac := range entries {
|
||||||
t.Errorf("préfixe MAC 00:22:33: absent :\n%s", content)
|
if !strings.HasPrefix(mac, "00:22:33:") {
|
||||||
|
t.Errorf("mac de %s sans le préfixe 00:22:33: : %s", ip, mac)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,21 +25,24 @@ func GenerateConfig(c Config) (string, map[string]string, error) {
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintf(&sb, "dhcp-option=3\n")
|
fmt.Fprintf(&sb, "dhcp-option=3\n")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(&sb, "dhcp-option=6,1.1.1.1,8.8.8.8\n\n")
|
fmt.Fprintf(&sb, "dhcp-option=6,1.1.1.1,8.8.8.8\n")
|
||||||
|
fmt.Fprintf(&sb, "dhcp-hostsdir=%s\n", HostsDir(c.ConfDir, c.Name))
|
||||||
|
fmt.Fprintf(&sb, "dhcp-optsdir=%s\n", OptsDir(c.ConfDir, c.Name))
|
||||||
|
|
||||||
entries := make(map[string]string)
|
entries := make(map[string]string)
|
||||||
i := 0
|
i := 0
|
||||||
for ip := cloneIP(c.Network.IP); c.Network.Contains(ip); incrementIP(ip) {
|
for ip := cloneIP(c.Network.IP); c.Network.Contains(ip); incrementIP(ip) {
|
||||||
mac := fmt.Sprintf("00:22:33:%02X:%02X:%02X", (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF)
|
entries[ip.String()] = fmt.Sprintf("00:22:33:%02X:%02X:%02X", (i>>16)&0xFF, (i>>8)&0xFF, i&0xFF)
|
||||||
fmt.Fprintf(&sb, "dhcp-host=%s,%s\n", mac, ip)
|
|
||||||
entries[ip.String()] = mac
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
outPath := filepath.Join(c.ConfDir, c.Name+".conf")
|
for _, dir := range []string{c.ConfDir, HostsDir(c.ConfDir, c.Name), OptsDir(c.ConfDir, c.Name)} {
|
||||||
if err := os.MkdirAll(c.ConfDir, 0755); err != nil {
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
return "", nil, err
|
return "", nil, fmt.Errorf("create %s: %w", dir, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outPath := filepath.Join(c.ConfDir, c.Name+".conf")
|
||||||
return outPath, entries, os.WriteFile(outPath, []byte(sb.String()), 0644)
|
return outPath, entries, os.WriteFile(outPath, []byte(sb.String()), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
69
internal/dhcp/reservation.go
Normal file
69
internal/dhcp/reservation.go
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
package dhcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Reservation struct {
|
||||||
|
MAC string
|
||||||
|
IP string
|
||||||
|
}
|
||||||
|
|
||||||
|
func HostsDir(confDir, name string) string {
|
||||||
|
return filepath.Join(confDir, name+".hosts.d")
|
||||||
|
}
|
||||||
|
|
||||||
|
func OptsDir(confDir, name string) string {
|
||||||
|
return filepath.Join(confDir, name+".opts.d")
|
||||||
|
}
|
||||||
|
|
||||||
|
func UnitName(name string) string {
|
||||||
|
return "dnsmasq@" + name + ".service"
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteReservations(confDir, name, vmName string, res []Reservation) error {
|
||||||
|
if len(res) == 0 {
|
||||||
|
return fmt.Errorf("no reservation for vm %q: it would get no address", vmName)
|
||||||
|
}
|
||||||
|
|
||||||
|
dir := HostsDir(confDir, name)
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("create %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
for _, r := range res {
|
||||||
|
if r.MAC == "" || r.IP == "" {
|
||||||
|
return fmt.Errorf("incomplete reservation for vm %q: mac=%q ip=%q", vmName, r.MAC, r.IP)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&sb, "%s,%s\n", r.MAC, r.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
path := filepath.Join(dir, vmName)
|
||||||
|
if err := os.WriteFile(path, []byte(sb.String()), 0644); err != nil {
|
||||||
|
return fmt.Errorf("write %s: %w", path, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveReservations(confDir, name, vmName string) error {
|
||||||
|
for _, dir := range []string{HostsDir(confDir, name), OptsDir(confDir, name)} {
|
||||||
|
path := filepath.Join(dir, vmName)
|
||||||
|
if err := os.Remove(path); err != nil && !os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("remove %s: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveSubnetDirs(confDir, name string) error {
|
||||||
|
for _, dir := range []string{HostsDir(confDir, name), OptsDir(confDir, name)} {
|
||||||
|
if err := os.RemoveAll(dir); err != nil {
|
||||||
|
return fmt.Errorf("remove %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
126
internal/dhcp/reservation_test.go
Normal file
126
internal/dhcp/reservation_test.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
package dhcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const subName = "vp-admin_br-000001"
|
||||||
|
|
||||||
|
func TestWriteReservations_WritesOneLinePerInterface(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
res := []Reservation{
|
||||||
|
{MAC: "00:22:33:00:01:02", IP: "10.1.1.2"},
|
||||||
|
{MAC: "00:22:33:00:02:07", IP: "10.1.2.7"},
|
||||||
|
}
|
||||||
|
if err := WriteReservations(dir, subName, "i-web", res); err != nil {
|
||||||
|
t.Fatalf("WriteReservations : %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filepath.Join(HostsDir(dir, subName), "i-web"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("fichier absent : %v", err)
|
||||||
|
}
|
||||||
|
want := "00:22:33:00:01:02,10.1.1.2\n00:22:33:00:02:07,10.1.2.7\n"
|
||||||
|
if string(content) != want {
|
||||||
|
t.Errorf("contenu attendu %q, obtenu %q", want, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteReservations_CreatesDir(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
if err := WriteReservations(dir, subName, "i-web", []Reservation{{MAC: "aa", IP: "10.0.0.1"}}); err != nil {
|
||||||
|
t.Fatalf("WriteReservations : %v", err)
|
||||||
|
}
|
||||||
|
if fi, err := os.Stat(HostsDir(dir, subName)); err != nil || !fi.IsDir() {
|
||||||
|
t.Errorf("hosts.d non créé : %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteReservations_EmptyIsAnError(t *testing.T) {
|
||||||
|
if err := WriteReservations(t.TempDir(), subName, "i-web", nil); err == nil {
|
||||||
|
t.Error("sans réservation la VM n'obtiendrait aucune adresse : il faut échouer, pas écrire un fichier vide")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteReservations_IncompleteIsAnError(t *testing.T) {
|
||||||
|
cases := []Reservation{{MAC: "", IP: "10.0.0.1"}, {MAC: "aa:bb", IP: ""}}
|
||||||
|
for _, r := range cases {
|
||||||
|
if err := WriteReservations(t.TempDir(), subName, "i-web", []Reservation{r}); err == nil {
|
||||||
|
t.Errorf("réservation incomplète acceptée : %+v", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteReservations_Overwrites(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
_ = WriteReservations(dir, subName, "i-web", []Reservation{{MAC: "aa", IP: "10.0.0.1"}})
|
||||||
|
if err := WriteReservations(dir, subName, "i-web", []Reservation{{MAC: "bb", IP: "10.0.0.2"}}); err != nil {
|
||||||
|
t.Fatalf("WriteReservations : %v", err)
|
||||||
|
}
|
||||||
|
content, _ := os.ReadFile(filepath.Join(HostsDir(dir, subName), "i-web"))
|
||||||
|
if string(content) != "bb,10.0.0.2\n" {
|
||||||
|
t.Errorf("la réécriture doit remplacer, obtenu %q", content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveReservations_RemovesBothFiles(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
_ = WriteReservations(dir, subName, "i-web", []Reservation{{MAC: "aa", IP: "10.0.0.1"}})
|
||||||
|
if err := os.MkdirAll(OptsDir(dir, subName), 0755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
optsFile := filepath.Join(OptsDir(dir, subName), "i-web")
|
||||||
|
if err := os.WriteFile(optsFile, []byte("tag:i-web,3,10.0.0.1\n"), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := RemoveReservations(dir, subName, "i-web"); err != nil {
|
||||||
|
t.Fatalf("RemoveReservations : %v", err)
|
||||||
|
}
|
||||||
|
for _, p := range []string{filepath.Join(HostsDir(dir, subName), "i-web"), optsFile} {
|
||||||
|
if _, err := os.Stat(p); !os.IsNotExist(err) {
|
||||||
|
t.Errorf("%s aurait dû être supprimé", p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveReservations_AbsentIsNotAnError(t *testing.T) {
|
||||||
|
if err := RemoveReservations(t.TempDir(), subName, "jamais-creee"); err != nil {
|
||||||
|
t.Errorf("supprimer une VM sans réservation ne doit pas échouer : %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveReservations_LeavesOtherVMs(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
_ = WriteReservations(dir, subName, "i-web", []Reservation{{MAC: "aa", IP: "10.0.0.1"}})
|
||||||
|
_ = WriteReservations(dir, subName, "i-db", []Reservation{{MAC: "bb", IP: "10.0.0.2"}})
|
||||||
|
|
||||||
|
if err := RemoveReservations(dir, subName, "i-web"); err != nil {
|
||||||
|
t.Fatalf("RemoveReservations : %v", err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(filepath.Join(HostsDir(dir, subName), "i-db")); err != nil {
|
||||||
|
t.Errorf("i-db ne devait pas être touchée : %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveSubnetDirs(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
_ = WriteReservations(dir, subName, "i-web", []Reservation{{MAC: "aa", IP: "10.0.0.1"}})
|
||||||
|
|
||||||
|
if err := RemoveSubnetDirs(dir, subName); err != nil {
|
||||||
|
t.Fatalf("RemoveSubnetDirs : %v", err)
|
||||||
|
}
|
||||||
|
for _, d := range []string{HostsDir(dir, subName), OptsDir(dir, subName)} {
|
||||||
|
if _, err := os.Stat(d); !os.IsNotExist(err) {
|
||||||
|
t.Errorf("%s aurait dû être supprimé", d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnitName(t *testing.T) {
|
||||||
|
if got := UnitName(subName); got != "dnsmasq@vp-admin_br-000001.service" {
|
||||||
|
t.Errorf("unit attendue dnsmasq@%s.service, obtenu %s", subName, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -68,6 +68,10 @@ func stopDHCP(db *badger.DB, subnetName string, d subnetData) error {
|
||||||
return fmt.Errorf("remove dnsmasq config: %w", err)
|
return fmt.Errorf("remove dnsmasq config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := dhcp.RemoveSubnetDirs(dhcp.DefaultConfDir, d.vpc+"_"+d.bridge); err != nil {
|
||||||
|
return fmt.Errorf("remove dnsmasq dirs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := kv.DeleteInDB(db, "subnet/"+subnetName+"/dhcp"); err != nil {
|
if err := kv.DeleteInDB(db, "subnet/"+subnetName+"/dhcp"); err != nil {
|
||||||
return fmt.Errorf("delete dhcp entries: %w", err)
|
return fmt.Errorf("delete dhcp entries: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
configuration "git.g3e.fr/syonad/two/internal/config/agent"
|
configuration "git.g3e.fr/syonad/two/internal/config/agent"
|
||||||
|
"git.g3e.fr/syonad/two/internal/dhcp"
|
||||||
"git.g3e.fr/syonad/two/internal/iptables"
|
"git.g3e.fr/syonad/two/internal/iptables"
|
||||||
"git.g3e.fr/syonad/two/internal/metadata"
|
"git.g3e.fr/syonad/two/internal/metadata"
|
||||||
"git.g3e.fr/syonad/two/internal/netif"
|
"git.g3e.fr/syonad/two/internal/netif"
|
||||||
|
|
@ -41,6 +42,11 @@ func StartVM(db *badger.DB, name string, cfg *configuration.Config) error {
|
||||||
return fmt.Errorf("add metadata redirect: %w", err)
|
return fmt.Errorf("add metadata redirect: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := dhcp.WriteReservations(dhcp.DefaultConfDir, d.vpcName+"_"+d.bridge, name,
|
||||||
|
[]dhcp.Reservation{{MAC: d.mac, IP: d.ip}}); err != nil {
|
||||||
|
return fmt.Errorf("write dhcp reservation: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
if err := metadata.StartMetadata(metadata.NoCloudConfig{
|
if err := metadata.StartMetadata(metadata.NoCloudConfig{
|
||||||
Name: name,
|
Name: name,
|
||||||
VpcName: d.vpcName,
|
VpcName: d.vpcName,
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
configuration "git.g3e.fr/syonad/two/internal/config/agent"
|
configuration "git.g3e.fr/syonad/two/internal/config/agent"
|
||||||
|
"git.g3e.fr/syonad/two/internal/dhcp"
|
||||||
"git.g3e.fr/syonad/two/internal/iptables"
|
"git.g3e.fr/syonad/two/internal/iptables"
|
||||||
"git.g3e.fr/syonad/two/internal/metadata"
|
"git.g3e.fr/syonad/two/internal/metadata"
|
||||||
"git.g3e.fr/syonad/two/internal/netif"
|
"git.g3e.fr/syonad/two/internal/netif"
|
||||||
"git.g3e.fr/syonad/two/internal/netns"
|
"git.g3e.fr/syonad/two/internal/netns"
|
||||||
"git.g3e.fr/syonad/two/internal/qmp"
|
"git.g3e.fr/syonad/two/internal/qmp"
|
||||||
"git.g3e.fr/syonad/two/internal/state"
|
"git.g3e.fr/syonad/two/internal/state"
|
||||||
|
"git.g3e.fr/syonad/two/pkg/systemd"
|
||||||
|
|
||||||
"github.com/dgraph-io/badger/v4"
|
"github.com/dgraph-io/badger/v4"
|
||||||
)
|
)
|
||||||
|
|
@ -45,7 +47,6 @@ func StopVM(db *badger.DB, name string, cfg *configuration.Config) error {
|
||||||
}
|
}
|
||||||
// socket absent ou QEMU déjà arrêté : cleanup direct
|
// socket absent ou QEMU déjà arrêté : cleanup direct
|
||||||
|
|
||||||
|
|
||||||
if err := netns.Call(d.vpcName, func() error {
|
if err := netns.Call(d.vpcName, func() error {
|
||||||
return iptables.DeleteMetadataRedirect(d.ip, d.interfaceIP, d.metadataPort)
|
return iptables.DeleteMetadataRedirect(d.ip, d.interfaceIP, d.metadataPort)
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
|
|
@ -60,6 +61,10 @@ func StopVM(db *badger.DB, name string, cfg *configuration.Config) error {
|
||||||
return fmt.Errorf("delete tap: %w", err)
|
return fmt.Errorf("delete tap: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := removeDHCPReservation(d, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if d.uefi {
|
if d.uefi {
|
||||||
varsPath := filepath.Join(cfg.QEMU.UEFIVarsDir, name+"-uefi-vars.fd")
|
varsPath := filepath.Join(cfg.QEMU.UEFIVarsDir, name+"-uefi-vars.fd")
|
||||||
os.Remove(varsPath)
|
os.Remove(varsPath)
|
||||||
|
|
@ -68,6 +73,38 @@ func StopVM(db *badger.DB, name string, cfg *configuration.Config) error {
|
||||||
return state.Set(db, "vm/"+name, state.Deleted)
|
return state.Set(db, "vm/"+name, state.Deleted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// removeDHCPReservation retire le fichier de réservation puis redémarre dnsmasq :
|
||||||
|
// un fichier ajouté dans un dhcp-hostsdir est relu à chaud, un fichier retiré ne
|
||||||
|
// l'est pas (vérifié sur dnsmasq 2.90).
|
||||||
|
func removeDHCPReservation(d vmData, name string) error {
|
||||||
|
confName := d.vpcName + "_" + d.bridge
|
||||||
|
|
||||||
|
if err := dhcp.RemoveReservations(dhcp.DefaultConfDir, confName, name); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
svc, err := systemd.New()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("connect to systemd: %w", err)
|
||||||
|
}
|
||||||
|
defer svc.Close()
|
||||||
|
|
||||||
|
unit := dhcp.UnitName(confName)
|
||||||
|
status, err := svc.Status(unit)
|
||||||
|
if err != nil || status.ActiveState != "active" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := svc.Restart(unit); err != nil {
|
||||||
|
return fmt.Errorf("restart %s: %w", unit, err)
|
||||||
|
}
|
||||||
|
if status, err := svc.Status(unit); err != nil {
|
||||||
|
return fmt.Errorf("status %s after restart: %w", unit, err)
|
||||||
|
} else if status.ActiveState != "active" {
|
||||||
|
return fmt.Errorf("%s is %s after restart", unit, status.ActiveState)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func waitQMPDead(socketPath string, timeout, poll time.Duration) {
|
func waitQMPDead(socketPath string, timeout, poll time.Duration) {
|
||||||
timer := time.After(timeout)
|
timer := time.After(timeout)
|
||||||
for {
|
for {
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,11 @@ func (m *Manager) Stop(service string) error {
|
||||||
return m.job("StopUnit", service)
|
return m.job("StopUnit", service)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restart redémarre un service systemd
|
||||||
|
func (m *Manager) Restart(service string) error {
|
||||||
|
return m.job("RestartUnit", service)
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Manager) job(method, service string) error {
|
func (m *Manager) job(method, service string) error {
|
||||||
callCtx, callCancel := context.WithTimeout(context.Background(), defaultTimeout)
|
callCtx, callCancel := context.WithTimeout(context.Background(), defaultTimeout)
|
||||||
defer callCancel()
|
defer callCancel()
|
||||||
|
|
@ -66,6 +71,8 @@ func (m *Manager) job(method, service string) error {
|
||||||
_, err = m.conn.StartUnitContext(callCtx, service, jobMode, ch)
|
_, err = m.conn.StartUnitContext(callCtx, service, jobMode, ch)
|
||||||
case "StopUnit":
|
case "StopUnit":
|
||||||
_, err = m.conn.StopUnitContext(callCtx, service, jobMode, ch)
|
_, err = m.conn.StopUnitContext(callCtx, service, jobMode, ch)
|
||||||
|
case "RestartUnit":
|
||||||
|
_, err = m.conn.RestartUnitContext(callCtx, service, jobMode, ch)
|
||||||
default:
|
default:
|
||||||
return errors.New("unsupported job method")
|
return errors.New("unsupported job method")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,10 @@ echo "start dnsmasq ${NETNS} ${BRIDGE}"
|
||||||
|
|
||||||
exec ip netns exec "${NETNS}" \
|
exec ip netns exec "${NETNS}" \
|
||||||
dnsmasq \
|
dnsmasq \
|
||||||
--no-daemon \
|
|
||||||
--interface="${BRIDGE}" \
|
--interface="${BRIDGE}" \
|
||||||
--bind-interfaces \
|
--bind-interfaces \
|
||||||
--pid-file="/run/dnsmasq-$arg.pid" \
|
--pid-file="/run/dnsmasq-$arg.pid" \
|
||||||
|
--dhcp-leasefile="/run/dnsmasq-$arg.leases" \
|
||||||
--conf-file="/etc/dnsmasq.d/$arg.conf" \
|
--conf-file="/etc/dnsmasq.d/$arg.conf" \
|
||||||
--no-hosts \
|
--no-hosts \
|
||||||
--no-resolv \
|
--no-resolv \
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue