mirror of
https://github.com/lxc/incus
synced 2026-08-02 05:26:46 +00:00
284 lines
7.1 KiB
Go
284 lines
7.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/csv"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/lxc/incus/v7/shared/subprocess"
|
|
)
|
|
|
|
func ovsConvert() ([][]string, error) {
|
|
commands := [][]string{}
|
|
|
|
output, err := subprocess.RunCommand("ovs-vsctl", "get", "open_vswitch", ".", "external-ids:ovn-bridge-mappings")
|
|
if err != nil {
|
|
// Assume that being unable to read the key means it's not set.
|
|
return nil, nil
|
|
}
|
|
|
|
oldValue := strings.TrimSpace(strings.ReplaceAll(output, "\"", ""))
|
|
oldBridges := []string{}
|
|
|
|
values := strings.Split(oldValue, ",")
|
|
for i, value := range values {
|
|
fields := strings.Split(value, ":")
|
|
oldBridges = append(oldBridges, fields[1])
|
|
fields[1] = strings.ReplaceAll(fields[1], "lxdovn", "incusovn")
|
|
values[i] = strings.Join(fields, ":")
|
|
}
|
|
|
|
newValue := strings.Join(values, ",")
|
|
|
|
if oldValue != newValue {
|
|
commands = append(commands, []string{"ovs-vsctl", "set", "open_vswitch", ".", fmt.Sprintf("external-ids:ovn-bridge-mappings=%s", newValue)})
|
|
}
|
|
|
|
for _, bridge := range oldBridges {
|
|
commands = append(commands, []string{"ovs-vsctl", "del-br", bridge})
|
|
}
|
|
|
|
return commands, nil
|
|
}
|
|
|
|
func ovnBackup(nbDB string, sbDB string, target string) error {
|
|
// Backup the Northbound database.
|
|
nbStdout, err := os.Create(filepath.Join(target, fmt.Sprintf("lxd-to-incus.ovn-nb.%d.backup", os.Getpid())))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer nbStdout.Close()
|
|
|
|
err = nbStdout.Chmod(0o600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
args := []string{"dump", "-f", "csv", nbDB, "OVN_Northbound"}
|
|
if strings.Contains(nbDB, "ssl:") {
|
|
args = append(args, "-c", "/etc/ovn/cert_host")
|
|
args = append(args, "-p", "/etc/ovn/key_host")
|
|
args = append(args, "-C", "/etc/ovn/ovn-central.crt")
|
|
}
|
|
|
|
err = subprocess.RunCommandWithFds(context.Background(), nil, nbStdout, "ovsdb-client", args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Backup the Southbound database.
|
|
sbStdout, err := os.Create(filepath.Join(target, fmt.Sprintf("lxd-to-incus.ovn-sb.%d.backup", os.Getpid())))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer sbStdout.Close()
|
|
|
|
err = sbStdout.Chmod(0o600)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
args = []string{"dump", "-f", "csv", sbDB, "OVN_Southbound"}
|
|
if strings.Contains(sbDB, "ssl:") {
|
|
args = append(args, "-c", "/etc/ovn/cert_host")
|
|
args = append(args, "-p", "/etc/ovn/key_host")
|
|
args = append(args, "-C", "/etc/ovn/ovn-central.crt")
|
|
}
|
|
|
|
err = subprocess.RunCommandWithFds(context.Background(), nil, sbStdout, "ovsdb-client", args...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func ovnConvert(nbDB string, sbDB string) ([][]string, error) {
|
|
commands := [][]string{}
|
|
|
|
// Patch the Northbound records.
|
|
args := []string{"dump", "-f", "csv", nbDB, "OVN_Northbound"}
|
|
if strings.Contains(sbDB, "ssl:") {
|
|
args = append(args, "-c", "/etc/ovn/cert_host")
|
|
args = append(args, "-p", "/etc/ovn/key_host")
|
|
args = append(args, "-C", "/etc/ovn/ovn-central.crt")
|
|
}
|
|
|
|
output, err := subprocess.RunCommand("ovsdb-client", args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data, err := ovnParseDump(output)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for table, records := range data {
|
|
for _, record := range records {
|
|
for k, v := range record {
|
|
needsFixing, newValue, err := ovnCheckValue(table, k, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if needsFixing {
|
|
cmd := []string{"ovn-nbctl", "--db", nbDB}
|
|
if strings.Contains(nbDB, "ssl:") {
|
|
cmd = append(cmd, "-c", "/etc/ovn/cert_host")
|
|
cmd = append(cmd, "-p", "/etc/ovn/key_host")
|
|
cmd = append(cmd, "-C", "/etc/ovn/ovn-central.crt")
|
|
}
|
|
|
|
cmd = append(cmd, []string{"set", table, record["_uuid"], fmt.Sprintf("%s=%s", k, newValue)}...)
|
|
commands = append(commands, cmd)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Patch the Southbound records.
|
|
args = []string{"dump", "-f", "csv", sbDB, "OVN_Southbound"}
|
|
if strings.Contains(sbDB, "ssl:") {
|
|
args = append(args, "-c", "/etc/ovn/cert_host")
|
|
args = append(args, "-p", "/etc/ovn/key_host")
|
|
args = append(args, "-C", "/etc/ovn/ovn-central.crt")
|
|
}
|
|
|
|
output, err = subprocess.RunCommand("ovsdb-client", args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
data, err = ovnParseDump(output)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for table, records := range data {
|
|
for _, record := range records {
|
|
for k, v := range record {
|
|
needsFixing, newValue, err := ovnCheckValue(table, k, v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if needsFixing {
|
|
cmd := []string{"ovn-sbctl", "--db", sbDB}
|
|
if strings.Contains(sbDB, "ssl:") {
|
|
cmd = append(cmd, "-c", "/etc/ovn/cert_host")
|
|
cmd = append(cmd, "-p", "/etc/ovn/key_host")
|
|
cmd = append(cmd, "-C", "/etc/ovn/ovn-central.crt")
|
|
}
|
|
|
|
cmd = append(cmd, []string{"set", table, record["_uuid"], fmt.Sprintf("%s=%s", k, newValue)}...)
|
|
commands = append(commands, cmd)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return commands, nil
|
|
}
|
|
|
|
func ovnCheckValue(table string, k string, v string) (bool, string, error) {
|
|
if !strings.Contains(v, "lxd") {
|
|
return false, "", nil
|
|
}
|
|
|
|
if table == "DNS" && k == "records" {
|
|
return false, "", nil
|
|
}
|
|
|
|
if table == "Chassis" && k == "other_config" {
|
|
return false, "", nil
|
|
}
|
|
|
|
if table == "Chassis" && k == "external_ids" {
|
|
return false, "", nil
|
|
}
|
|
|
|
if table == "Logical_Flow" && k == "actions" {
|
|
return false, "", nil
|
|
}
|
|
|
|
if table == "DHCP_Options" && k == "options" {
|
|
return false, "", nil
|
|
}
|
|
|
|
if table == "Logical_Router_Port" && k == "ipv6_ra_configs" {
|
|
return false, "", nil
|
|
}
|
|
|
|
if (table == "Logical_Switch_Port" || table == "Port_Binding") && k == "options" && (v == "{network_name=lxdbr0}" || v == "{network_name=lxdbr1}") {
|
|
return false, "", nil
|
|
}
|
|
|
|
newValue := strings.ReplaceAll(v, "lxd-net", "incus-net")
|
|
newValue = strings.ReplaceAll(newValue, "lxd_acl", "incus_acl")
|
|
newValue = strings.ReplaceAll(newValue, "lxd_location", "incus_location")
|
|
newValue = strings.ReplaceAll(newValue, "lxd_net", "incus_net")
|
|
newValue = strings.ReplaceAll(newValue, "lxd_port_group", "incus_port_group")
|
|
newValue = strings.ReplaceAll(newValue, "lxd_project_id", "incus_project_id")
|
|
newValue = strings.ReplaceAll(newValue, "lxd_switch", "incus_switch")
|
|
newValue = strings.ReplaceAll(newValue, "lxd_switch_port", "incus_switch_port")
|
|
|
|
if v == newValue {
|
|
return true, "", fmt.Errorf("Couldn't convert value %q for key %q in table %q", v, k, table)
|
|
}
|
|
|
|
return true, newValue, nil
|
|
}
|
|
|
|
func ovnParseDump(data string) (map[string][]map[string]string, error) {
|
|
output := map[string][]map[string]string{}
|
|
|
|
tableName := ""
|
|
fields := []string{}
|
|
newTable := false
|
|
for _, line := range strings.Split(data, "\n") {
|
|
if line == "" {
|
|
continue
|
|
}
|
|
|
|
if !strings.Contains(line, ",") && strings.HasSuffix(line, " table") {
|
|
newTable = true
|
|
tableName = strings.Split(line, " ")[0]
|
|
output[tableName] = []map[string]string{}
|
|
continue
|
|
}
|
|
|
|
if newTable {
|
|
newTable = false
|
|
|
|
var err error
|
|
fields, err = csv.NewReader(strings.NewReader(line)).Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
continue
|
|
}
|
|
|
|
record := map[string]string{}
|
|
|
|
entry, err := csv.NewReader(strings.NewReader(line)).Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for k, v := range entry {
|
|
record[fields[k]] = v
|
|
}
|
|
|
|
output[tableName] = append(output[tableName], record)
|
|
}
|
|
|
|
return output, nil
|
|
}
|