[lxc-devel] [lxd/master] Validate: Makes shared/validate helpers non-optional
tomponline on Github
lxc-bot at linuxcontainers.org
Mon Aug 3 13:13:57 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 491 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200803/e9629b0d/attachment-0001.bin>
-------------- next part --------------
From a573f047f39f03f45a8a1af62716cf69e3e18c35 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:48:56 +0100
Subject: [PATCH 01/33] shared/validate: Makes IsUint32 non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 586f0c9e35..24da1237db 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -57,10 +57,6 @@ func IsUint8(value string) error {
// IsUint32 validates whether the string can be converted to an uint32.
func IsUint32(value string) error {
- if value == "" {
- return nil
- }
-
_, err := strconv.ParseUint(value, 10, 32)
if err != nil {
return fmt.Errorf("Invalid value for uint32 %q: %v", value, err)
From faa11bdadb31ce900e453a2a6fb55230584ac88f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:49:19 +0100
Subject: [PATCH 02/33] lxd: Wraps validate.IsUint32 in validate.Optional
Due to validate.IsUint32 now being non-optional.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/api_project.go | 8 ++++----
lxd/device/disk.go | 2 +-
lxd/device/nic.go | 6 +++---
lxd/storage/drivers/driver_lvm.go | 2 +-
lxd/storage/drivers/driver_lvm_volumes.go | 2 +-
lxd/storage_pools_config.go | 2 +-
6 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/lxd/api_project.go b/lxd/api_project.go
index 82e757fbb8..01e2256aeb 100644
--- a/lxd/api_project.go
+++ b/lxd/api_project.go
@@ -526,11 +526,11 @@ var projectConfigKeys = map[string]func(value string) error{
"features.profiles": validate.IsBool,
"features.images": validate.IsBool,
"features.storage.volumes": validate.IsBool,
- "limits.containers": validate.IsUint32,
- "limits.virtual-machines": validate.IsUint32,
+ "limits.containers": validate.Optional(validate.IsUint32),
+ "limits.virtual-machines": validate.Optional(validate.IsUint32),
"limits.memory": validate.IsSize,
- "limits.processes": validate.IsUint32,
- "limits.cpu": validate.IsUint32,
+ "limits.processes": validate.Optional(validate.IsUint32),
+ "limits.cpu": validate.Optional(validate.IsUint32),
"limits.disk": validate.IsSize,
"restricted": validate.IsBool,
"restricted.containers.nesting": isEitherAllowOrBlock,
diff --git a/lxd/device/disk.go b/lxd/device/disk.go
index 6c3f7a144f..051753f57f 100644
--- a/lxd/device/disk.go
+++ b/lxd/device/disk.go
@@ -90,7 +90,7 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
"raw.mount.options": validate.IsAny,
"ceph.cluster_name": validate.IsAny,
"ceph.user_name": validate.IsAny,
- "boot.priority": validate.IsUint32,
+ "boot.priority": validate.Optional(validate.IsUint32),
"path": validate.IsAny,
}
diff --git a/lxd/device/nic.go b/lxd/device/nic.go
index 09ec02cf23..6e933bc443 100644
--- a/lxd/device/nic.go
+++ b/lxd/device/nic.go
@@ -27,13 +27,13 @@ func nicValidationRules(requiredFields []string, optionalFields []string) map[st
"ipv6.address": validate.IsNetworkAddressV6,
"ipv4.routes": validate.IsNetworkV4List,
"ipv6.routes": validate.IsNetworkV6List,
- "boot.priority": validate.IsUint32,
+ "boot.priority": validate.Optional(validate.IsUint32),
"ipv4.gateway": networkValidGateway,
"ipv6.gateway": networkValidGateway,
"ipv4.host_address": validate.IsNetworkAddressV4,
"ipv6.host_address": validate.IsNetworkAddressV6,
- "ipv4.host_table": validate.IsUint32,
- "ipv6.host_table": validate.IsUint32,
+ "ipv4.host_table": validate.Optional(validate.IsUint32),
+ "ipv6.host_table": validate.Optional(validate.IsUint32),
}
validators := map[string]func(value string) error{}
diff --git a/lxd/storage/drivers/driver_lvm.go b/lxd/storage/drivers/driver_lvm.go
index 598ea3fe7d..8798a3076e 100644
--- a/lxd/storage/drivers/driver_lvm.go
+++ b/lxd/storage/drivers/driver_lvm.go
@@ -439,7 +439,7 @@ func (d *lvm) Validate(config map[string]string) error {
}
return validate.IsOneOf(value, lvmAllowedFilesystems)
},
- "volume.lvm.stripes": validate.IsUint32,
+ "volume.lvm.stripes": validate.Optional(validate.IsUint32),
"volume.lvm.stripes.size": validate.IsSize,
"lvm.vg.force_reuse": validate.IsBool,
}
diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go
index 2ea0c39022..2d9c730076 100644
--- a/lxd/storage/drivers/driver_lvm_volumes.go
+++ b/lxd/storage/drivers/driver_lvm_volumes.go
@@ -248,7 +248,7 @@ func (d *lvm) ValidateVolume(vol Volume, removeUnknownKeys bool) error {
}
return validate.IsOneOf(value, lvmAllowedFilesystems)
},
- "lvm.stripes": validate.IsUint32,
+ "lvm.stripes": validate.Optional(validate.IsUint32),
"lvm.stripes.size": validate.IsSize,
}
diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go
index 1e0d5fef83..0dc57cf710 100644
--- a/lxd/storage_pools_config.go
+++ b/lxd/storage_pools_config.go
@@ -46,7 +46,7 @@ var storagePoolConfigKeys = map[string]func(value string) error{
"lvm.thinpool_name": validate.IsAny,
"lvm.use_thinpool": validate.IsBool,
"lvm.vg_name": validate.IsAny,
- "volume.lvm.stripes": validate.IsUint32,
+ "volume.lvm.stripes": validate.Optional(validate.IsUint32),
"volume.lvm.stripes.size": validate.IsSize,
"lvm.vg.force_reuse": validate.IsBool,
From 5b1320ad82804759b223c20ab45a31dbe4ba9c86 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:49:54 +0100
Subject: [PATCH 03/33] shared/instance: Wraps validate.IsUint32 in
validate.Optional
Due to validate.IsUint32 now being non-optional.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/instance.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/shared/instance.go b/shared/instance.go
index 9ba5760dcf..a565a3cbab 100644
--- a/shared/instance.go
+++ b/shared/instance.go
@@ -177,8 +177,8 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
"linux.kernel_modules": validate.IsAny,
"migration.incremental.memory": validate.IsBool,
- "migration.incremental.memory.iterations": validate.IsUint32,
- "migration.incremental.memory.goal": validate.IsUint32,
+ "migration.incremental.memory.iterations": validate.Optional(validate.IsUint32),
+ "migration.incremental.memory.goal": validate.Optional(validate.IsUint32),
"nvidia.runtime": validate.IsBool,
"nvidia.driver.capabilities": validate.IsAny,
@@ -193,9 +193,9 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
"security.protection.delete": validate.IsBool,
"security.protection.shift": validate.IsBool,
- "security.idmap.base": validate.IsUint32,
+ "security.idmap.base": validate.Optional(validate.IsUint32),
"security.idmap.isolated": validate.IsBool,
- "security.idmap.size": validate.IsUint32,
+ "security.idmap.size": validate.Optional(validate.IsUint32),
"security.secureboot": validate.IsBool,
From f6bbb88c5ce216d22dba32e01996be10169b0171 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:50:44 +0100
Subject: [PATCH 04/33] shared/validate: Makes IsUint8 non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 24da1237db..49d944fef1 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -43,10 +43,6 @@ func IsInt64(value string) error {
// IsUint8 validates whether the string can be converted to an uint8.
func IsUint8(value string) error {
- if value == "" {
- return nil
- }
-
_, err := strconv.ParseUint(value, 10, 8)
if err != nil {
return fmt.Errorf("Invalid value for an integer %q. Must be between 0 and 255", value)
From a96a34a715b27c69df173ac2d3738c81d75dbea5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:51:22 +0100
Subject: [PATCH 05/33] lxd/network/driver/bridge: Wraps validate.IsUint8 in
validate.Optional
Due to validate.IsUint32 now being non-optional.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/driver_bridge.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index d0afd95f55..6199ed65fe 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -257,7 +257,7 @@ func (n *bridge) Validate(config map[string]string) error {
case "inteface":
rules[k] = ValidNetworkName
case "ttl":
- rules[k] = validate.IsUint8
+ rules[k] = validate.Optional(validate.IsUint8)
}
}
}
From 0a2316a0b4d3d8530504f87d3714278efa96e28f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:52:26 +0100
Subject: [PATCH 06/33] shared/validate: Makes IsPriority non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 49d944fef1..08fab2c1ff 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -63,10 +63,6 @@ func IsUint32(value string) error {
// IsPriority validates priority number.
func IsPriority(value string) error {
- if value == "" {
- return nil
- }
-
valueInt, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return fmt.Errorf("Invalid value for an integer %q", value)
From de8ae02f9189b234816aa98d6e08d33f07853f14 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:53:20 +0100
Subject: [PATCH 07/33] shared/instance: Wraps validate.IsPriority in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/instance.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/shared/instance.go b/shared/instance.go
index a565a3cbab..306bd58fac 100644
--- a/shared/instance.go
+++ b/shared/instance.go
@@ -133,9 +133,9 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
return nil
},
- "limits.cpu.priority": validate.IsPriority,
+ "limits.cpu.priority": validate.Optional(validate.IsPriority),
- "limits.disk.priority": validate.IsPriority,
+ "limits.disk.priority": validate.Optional(validate.IsPriority),
"limits.hugepages.64KB": validate.IsSize,
"limits.hugepages.1MB": validate.IsSize,
@@ -167,10 +167,10 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
return validate.IsOneOf(value, []string{"soft", "hard"})
},
"limits.memory.swap": validate.IsBool,
- "limits.memory.swap.priority": validate.IsPriority,
+ "limits.memory.swap.priority": validate.Optional(validate.IsPriority),
"limits.memory.hugepages": validate.IsBool,
- "limits.network.priority": validate.IsPriority,
+ "limits.network.priority": validate.Optional(validate.IsPriority),
"limits.processes": validate.Optional(validate.IsInt64),
From 52ce0d90f816f0bee4b99ed993b9dc4e1772b60c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:54:06 +0100
Subject: [PATCH 08/33] shared/validate: Makes IsBool non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 08fab2c1ff..cacdb38ab1 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -77,10 +77,6 @@ func IsPriority(value string) error {
// IsBool validates if string can be understood as a bool.
func IsBool(value string) error {
- if value == "" {
- return nil
- }
-
if !stringInSlice(strings.ToLower(value), []string{"true", "false", "yes", "no", "1", "0", "on", "off"}) {
return fmt.Errorf("Invalid value for a boolean %q", value)
}
From c7b46cd66149b0cb4d8e6091fff8bcb3b33eea7b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:55:31 +0100
Subject: [PATCH 09/33] lxd: Wraps validate.IsBool in validate.Optional
Due to IsBool being required now.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/api_project.go | 8 ++++----
lxd/device/disk.go | 10 +++++-----
lxd/device/proxy.go | 4 ++--
lxd/device/unix_common.go | 2 +-
lxd/device/unix_hotplug.go | 2 +-
lxd/device/usb.go | 2 +-
lxd/network/driver_bridge.go | 18 +++++++++---------
lxd/storage/drivers/driver_ceph.go | 4 ++--
lxd/storage/drivers/driver_lvm.go | 4 ++--
lxd/storage/drivers/driver_zfs.go | 6 +++---
lxd/storage/drivers/driver_zfs_volumes.go | 4 ++--
lxd/storage/utils.go | 12 ++++++------
lxd/storage_pools_config.go | 14 +++++++-------
13 files changed, 45 insertions(+), 45 deletions(-)
diff --git a/lxd/api_project.go b/lxd/api_project.go
index 01e2256aeb..c4541ac8c4 100644
--- a/lxd/api_project.go
+++ b/lxd/api_project.go
@@ -523,16 +523,16 @@ func isEitherAllowOrBlockOrManaged(value string) error {
// Validate the project configuration
var projectConfigKeys = map[string]func(value string) error{
- "features.profiles": validate.IsBool,
- "features.images": validate.IsBool,
- "features.storage.volumes": validate.IsBool,
+ "features.profiles": validate.Optional(validate.IsBool),
+ "features.images": validate.Optional(validate.IsBool),
+ "features.storage.volumes": validate.Optional(validate.IsBool),
"limits.containers": validate.Optional(validate.IsUint32),
"limits.virtual-machines": validate.Optional(validate.IsUint32),
"limits.memory": validate.IsSize,
"limits.processes": validate.Optional(validate.IsUint32),
"limits.cpu": validate.Optional(validate.IsUint32),
"limits.disk": validate.IsSize,
- "restricted": validate.IsBool,
+ "restricted": validate.Optional(validate.IsBool),
"restricted.containers.nesting": isEitherAllowOrBlock,
"restricted.containers.lowlevel": isEitherAllowOrBlock,
"restricted.containers.privilege": func(value string) error {
diff --git a/lxd/device/disk.go b/lxd/device/disk.go
index 051753f57f..790618cc4a 100644
--- a/lxd/device/disk.go
+++ b/lxd/device/disk.go
@@ -75,11 +75,11 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
}
rules := map[string]func(string) error{
- "required": validate.IsBool,
- "optional": validate.IsBool, // "optional" is deprecated, replaced by "required".
- "readonly": validate.IsBool,
- "recursive": validate.IsBool,
- "shift": validate.IsBool,
+ "required": validate.Optional(validate.IsBool),
+ "optional": validate.Optional(validate.IsBool), // "optional" is deprecated, replaced by "required".
+ "readonly": validate.Optional(validate.IsBool),
+ "recursive": validate.Optional(validate.IsBool),
+ "shift": validate.Optional(validate.IsBool),
"source": validate.IsAny,
"limits.read": validate.IsAny,
"limits.write": validate.IsAny,
diff --git a/lxd/device/proxy.go b/lxd/device/proxy.go
index 349d2307b6..c3ef87019c 100644
--- a/lxd/device/proxy.go
+++ b/lxd/device/proxy.go
@@ -73,12 +73,12 @@ func (d *proxy) validateConfig(instConf instance.ConfigReader) error {
"connect": validateAddr,
"bind": validateBind,
"mode": unixValidOctalFileMode,
- "nat": validate.IsBool,
+ "nat": validate.Optional(validate.IsBool),
"gid": unixValidUserID,
"uid": unixValidUserID,
"security.uid": unixValidUserID,
"security.gid": unixValidUserID,
- "proxy_protocol": validate.IsBool,
+ "proxy_protocol": validate.Optional(validate.IsBool),
}
err := d.config.Validate(rules)
diff --git a/lxd/device/unix_common.go b/lxd/device/unix_common.go
index 80a369521c..156e5d2600 100644
--- a/lxd/device/unix_common.go
+++ b/lxd/device/unix_common.go
@@ -54,7 +54,7 @@ func (d *unixCommon) validateConfig(instConf instance.ConfigReader) error {
"uid": unixValidUserID,
"gid": unixValidUserID,
"mode": unixValidOctalFileMode,
- "required": validate.IsBool,
+ "required": validate.Optional(validate.IsBool),
}
err := d.config.Validate(rules)
diff --git a/lxd/device/unix_hotplug.go b/lxd/device/unix_hotplug.go
index dd7db877e7..71f62b6f0b 100644
--- a/lxd/device/unix_hotplug.go
+++ b/lxd/device/unix_hotplug.go
@@ -53,7 +53,7 @@ func (d *unixHotplug) validateConfig(instConf instance.ConfigReader) error {
"uid": unixValidUserID,
"gid": unixValidUserID,
"mode": unixValidOctalFileMode,
- "required": validate.IsBool,
+ "required": validate.Optional(validate.IsBool),
}
err := d.config.Validate(rules)
diff --git a/lxd/device/usb.go b/lxd/device/usb.go
index 7dcfba9626..155e62cec6 100644
--- a/lxd/device/usb.go
+++ b/lxd/device/usb.go
@@ -55,7 +55,7 @@ func (d *usb) validateConfig(instConf instance.ConfigReader) error {
"uid": unixValidUserID,
"gid": unixValidUserID,
"mode": unixValidOctalFileMode,
- "required": validate.IsBool,
+ "required": validate.Optional(validate.IsBool),
}
err := d.config.Validate(rules)
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 6199ed65fe..0d466a3203 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -177,18 +177,18 @@ func (n *bridge) Validate(config map[string]string) error {
return validate.IsNetworkAddressCIDRV4(value)
},
- "ipv4.firewall": validate.IsBool,
- "ipv4.nat": validate.IsBool,
+ "ipv4.firewall": validate.Optional(validate.IsBool),
+ "ipv4.nat": validate.Optional(validate.IsBool),
"ipv4.nat.order": func(value string) error {
return validate.IsOneOf(value, []string{"before", "after"})
},
"ipv4.nat.address": validate.IsNetworkAddressV4,
- "ipv4.dhcp": validate.IsBool,
+ "ipv4.dhcp": validate.Optional(validate.IsBool),
"ipv4.dhcp.gateway": validate.IsNetworkAddressV4,
"ipv4.dhcp.expiry": validate.IsAny,
"ipv4.dhcp.ranges": validate.IsAny,
"ipv4.routes": validate.IsNetworkV4List,
- "ipv4.routing": validate.IsBool,
+ "ipv4.routing": validate.Optional(validate.IsBool),
"ipv6.address": func(value string) error {
if validate.IsOneOf(value, []string{"none", "auto"}) == nil {
@@ -197,18 +197,18 @@ func (n *bridge) Validate(config map[string]string) error {
return validate.IsNetworkAddressCIDRV6(value)
},
- "ipv6.firewall": validate.IsBool,
- "ipv6.nat": validate.IsBool,
+ "ipv6.firewall": validate.Optional(validate.IsBool),
+ "ipv6.nat": validate.Optional(validate.IsBool),
"ipv6.nat.order": func(value string) error {
return validate.IsOneOf(value, []string{"before", "after"})
},
"ipv6.nat.address": validate.IsNetworkAddressV6,
- "ipv6.dhcp": validate.IsBool,
+ "ipv6.dhcp": validate.Optional(validate.IsBool),
"ipv6.dhcp.expiry": validate.IsAny,
- "ipv6.dhcp.stateful": validate.IsBool,
+ "ipv6.dhcp.stateful": validate.Optional(validate.IsBool),
"ipv6.dhcp.ranges": validate.IsAny,
"ipv6.routes": validate.IsNetworkV6List,
- "ipv6.routing": validate.IsBool,
+ "ipv6.routing": validate.Optional(validate.IsBool),
"dns.domain": validate.IsAny,
"dns.search": validate.IsAny,
diff --git a/lxd/storage/drivers/driver_ceph.go b/lxd/storage/drivers/driver_ceph.go
index f5d7da67c2..f0b97bcdce 100644
--- a/lxd/storage/drivers/driver_ceph.go
+++ b/lxd/storage/drivers/driver_ceph.go
@@ -239,11 +239,11 @@ func (d *ceph) Delete(op *operations.Operation) error {
func (d *ceph) Validate(config map[string]string) error {
rules := map[string]func(value string) error{
"ceph.cluster_name": validate.IsAny,
- "ceph.osd.force_reuse": validate.IsBool,
+ "ceph.osd.force_reuse": validate.Optional(validate.IsBool),
"ceph.osd.pg_num": validate.IsAny,
"ceph.osd.pool_name": validate.IsAny,
"ceph.osd.data_pool_name": validate.IsAny,
- "ceph.rbd.clone_copy": validate.IsBool,
+ "ceph.rbd.clone_copy": validate.Optional(validate.IsBool),
"ceph.user.name": validate.IsAny,
"volatile.pool.pristine": validate.IsAny,
"volume.block.filesystem": func(value string) error {
diff --git a/lxd/storage/drivers/driver_lvm.go b/lxd/storage/drivers/driver_lvm.go
index 8798a3076e..5f25525ae9 100644
--- a/lxd/storage/drivers/driver_lvm.go
+++ b/lxd/storage/drivers/driver_lvm.go
@@ -431,7 +431,7 @@ func (d *lvm) Validate(config map[string]string) error {
rules := map[string]func(value string) error{
"lvm.vg_name": validate.IsAny,
"lvm.thinpool_name": validate.IsAny,
- "lvm.use_thinpool": validate.IsBool,
+ "lvm.use_thinpool": validate.Optional(validate.IsBool),
"volume.block.mount_options": validate.IsAny,
"volume.block.filesystem": func(value string) error {
if value == "" {
@@ -441,7 +441,7 @@ func (d *lvm) Validate(config map[string]string) error {
},
"volume.lvm.stripes": validate.Optional(validate.IsUint32),
"volume.lvm.stripes.size": validate.IsSize,
- "lvm.vg.force_reuse": validate.IsBool,
+ "lvm.vg.force_reuse": validate.Optional(validate.IsBool),
}
err := d.validatePool(config, rules)
diff --git a/lxd/storage/drivers/driver_zfs.go b/lxd/storage/drivers/driver_zfs.go
index 9d9fbde25e..73be97127d 100644
--- a/lxd/storage/drivers/driver_zfs.go
+++ b/lxd/storage/drivers/driver_zfs.go
@@ -343,9 +343,9 @@ func (d *zfs) Delete(op *operations.Operation) error {
func (d *zfs) Validate(config map[string]string) error {
rules := map[string]func(value string) error{
"zfs.pool_name": validate.IsAny,
- "zfs.clone_copy": validate.IsBool,
- "volume.zfs.remove_snapshots": validate.IsBool,
- "volume.zfs.use_refquota": validate.IsBool,
+ "zfs.clone_copy": validate.Optional(validate.IsBool),
+ "volume.zfs.remove_snapshots": validate.Optional(validate.IsBool),
+ "volume.zfs.use_refquota": validate.Optional(validate.IsBool),
}
return d.validatePool(config, rules)
diff --git a/lxd/storage/drivers/driver_zfs_volumes.go b/lxd/storage/drivers/driver_zfs_volumes.go
index a32c05df3c..082ae886d2 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -796,8 +796,8 @@ func (d *zfs) HasVolume(vol Volume) bool {
// ValidateVolume validates the supplied volume config.
func (d *zfs) ValidateVolume(vol Volume, removeUnknownKeys bool) error {
rules := map[string]func(value string) error{
- "zfs.remove_snapshots": validate.IsBool,
- "zfs.use_refquota": validate.IsBool,
+ "zfs.remove_snapshots": validate.Optional(validate.IsBool),
+ "zfs.use_refquota": validate.Optional(validate.IsBool),
}
return d.validateVolume(vol, rules, removeUnknownKeys)
diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index 9c93f8e69c..27be7d084b 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -251,10 +251,10 @@ var StorageVolumeConfigKeys = map[string]func(value string) ([]string, error){
return []string{"ceph", "lvm"}, validate.IsAny(value)
},
"security.shifted": func(value string) ([]string, error) {
- return SupportedPoolTypes, validate.IsBool(value)
+ return SupportedPoolTypes, validate.Optional(validate.IsBool)(value)
},
"security.unmapped": func(value string) ([]string, error) {
- return SupportedPoolTypes, validate.IsBool(value)
+ return SupportedPoolTypes, validate.Optional(validate.IsBool)(value)
},
"size": func(value string) ([]string, error) {
if value == "" {
@@ -275,7 +275,7 @@ var StorageVolumeConfigKeys = map[string]func(value string) ([]string, error){
return SupportedPoolTypes, validate.IsAny(value)
},
"zfs.remove_snapshots": func(value string) ([]string, error) {
- err := validate.IsBool(value)
+ err := validate.Optional(validate.IsBool)(value)
if err != nil {
return nil, err
}
@@ -283,7 +283,7 @@ var StorageVolumeConfigKeys = map[string]func(value string) ([]string, error){
return []string{"zfs"}, nil
},
"zfs.use_refquota": func(value string) ([]string, error) {
- err := validate.IsBool(value)
+ err := validate.Optional(validate.IsBool)(value)
if err != nil {
return nil, err
}
@@ -473,8 +473,8 @@ func validateVolumeCommonRules(vol drivers.Volume) map[string]func(string) error
// security.shifted and security.unmapped are only relevant for custom volumes.
if vol.Type() == drivers.VolumeTypeCustom {
- rules["security.shifted"] = validate.IsBool
- rules["security.unmapped"] = validate.IsBool
+ rules["security.shifted"] = validate.Optional(validate.IsBool)
+ rules["security.unmapped"] = validate.Optional(validate.IsBool)
}
// volatile.rootfs.size is only used for image volumes.
diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go
index 0dc57cf710..2231de9985 100644
--- a/lxd/storage_pools_config.go
+++ b/lxd/storage_pools_config.go
@@ -23,7 +23,7 @@ var storagePoolConfigKeys = map[string]func(value string) error{
// valid drivers: ceph
"ceph.cluster_name": validate.IsAny,
- "ceph.osd.force_reuse": validate.IsBool,
+ "ceph.osd.force_reuse": validate.Optional(validate.IsBool),
"ceph.osd.pool_name": validate.IsAny,
"ceph.osd.data_pool_name": validate.IsAny,
"ceph.osd.pg_num": func(value string) error {
@@ -34,7 +34,7 @@ var storagePoolConfigKeys = map[string]func(value string) error{
_, err := units.ParseByteSizeString(value)
return err
},
- "ceph.rbd.clone_copy": validate.IsBool,
+ "ceph.rbd.clone_copy": validate.Optional(validate.IsBool),
"ceph.user.name": validate.IsAny,
// valid drivers: cephfs
@@ -44,11 +44,11 @@ var storagePoolConfigKeys = map[string]func(value string) error{
// valid drivers: lvm
"lvm.thinpool_name": validate.IsAny,
- "lvm.use_thinpool": validate.IsBool,
+ "lvm.use_thinpool": validate.Optional(validate.IsBool),
"lvm.vg_name": validate.IsAny,
"volume.lvm.stripes": validate.Optional(validate.IsUint32),
"volume.lvm.stripes.size": validate.IsSize,
- "lvm.vg.force_reuse": validate.IsBool,
+ "lvm.vg.force_reuse": validate.Optional(validate.IsBool),
// valid drivers: btrfs, lvm, zfs
"size": validate.IsSize,
@@ -74,11 +74,11 @@ var storagePoolConfigKeys = map[string]func(value string) error{
"volume.size": validate.IsSize,
// valid drivers: zfs
- "volume.zfs.remove_snapshots": validate.IsBool,
- "volume.zfs.use_refquota": validate.IsBool,
+ "volume.zfs.remove_snapshots": validate.Optional(validate.IsBool),
+ "volume.zfs.use_refquota": validate.Optional(validate.IsBool),
// valid drivers: zfs
- "zfs.clone_copy": validate.IsBool,
+ "zfs.clone_copy": validate.Optional(validate.IsBool),
"zfs.pool_name": validate.IsAny,
"rsync.bwlimit": validate.IsAny,
}
From 9c934239621ef9fc921a45e38768533fc03b573e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 11:57:57 +0100
Subject: [PATCH 10/33] shared/instance: Wraps validate.IsBool in
validate.Optional
Due to IsBool being required now.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/instance.go | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/shared/instance.go b/shared/instance.go
index 306bd58fac..b596cbae6b 100644
--- a/shared/instance.go
+++ b/shared/instance.go
@@ -71,7 +71,7 @@ var HugePageSizeSuffix = [...]string{"64KB", "1MB", "2MB", "1GB"}
// to an appropriate checker function, which validates whether or not a
// given value is syntactically legal.
var KnownInstanceConfigKeys = map[string]func(value string) error{
- "boot.autostart": validate.IsBool,
+ "boot.autostart": validate.Optional(validate.IsBool),
"boot.autostart.delay": validate.Optional(validate.IsInt64),
"boot.autostart.priority": validate.Optional(validate.IsInt64),
"boot.stop.priority": validate.Optional(validate.IsInt64),
@@ -166,9 +166,9 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
"limits.memory.enforce": func(value string) error {
return validate.IsOneOf(value, []string{"soft", "hard"})
},
- "limits.memory.swap": validate.IsBool,
+ "limits.memory.swap": validate.Optional(validate.IsBool),
"limits.memory.swap.priority": validate.Optional(validate.IsPriority),
- "limits.memory.hugepages": validate.IsBool,
+ "limits.memory.hugepages": validate.Optional(validate.IsBool),
"limits.network.priority": validate.Optional(validate.IsPriority),
@@ -176,42 +176,42 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
"linux.kernel_modules": validate.IsAny,
- "migration.incremental.memory": validate.IsBool,
+ "migration.incremental.memory": validate.Optional(validate.IsBool),
"migration.incremental.memory.iterations": validate.Optional(validate.IsUint32),
"migration.incremental.memory.goal": validate.Optional(validate.IsUint32),
- "nvidia.runtime": validate.IsBool,
+ "nvidia.runtime": validate.Optional(validate.IsBool),
"nvidia.driver.capabilities": validate.IsAny,
"nvidia.require.cuda": validate.IsAny,
"nvidia.require.driver": validate.IsAny,
- "security.nesting": validate.IsBool,
- "security.privileged": validate.IsBool,
- "security.devlxd": validate.IsBool,
- "security.devlxd.images": validate.IsBool,
+ "security.nesting": validate.Optional(validate.IsBool),
+ "security.privileged": validate.Optional(validate.IsBool),
+ "security.devlxd": validate.Optional(validate.IsBool),
+ "security.devlxd.images": validate.Optional(validate.IsBool),
- "security.protection.delete": validate.IsBool,
- "security.protection.shift": validate.IsBool,
+ "security.protection.delete": validate.Optional(validate.IsBool),
+ "security.protection.shift": validate.Optional(validate.IsBool),
"security.idmap.base": validate.Optional(validate.IsUint32),
- "security.idmap.isolated": validate.IsBool,
+ "security.idmap.isolated": validate.Optional(validate.IsBool),
"security.idmap.size": validate.Optional(validate.IsUint32),
- "security.secureboot": validate.IsBool,
+ "security.secureboot": validate.Optional(validate.IsBool),
"security.syscalls.allow": validate.IsAny,
- "security.syscalls.blacklist_default": validate.IsBool,
- "security.syscalls.blacklist_compat": validate.IsBool,
+ "security.syscalls.blacklist_default": validate.Optional(validate.IsBool),
+ "security.syscalls.blacklist_compat": validate.Optional(validate.IsBool),
"security.syscalls.blacklist": validate.IsAny,
- "security.syscalls.deny_default": validate.IsBool,
- "security.syscalls.deny_compat": validate.IsBool,
+ "security.syscalls.deny_default": validate.Optional(validate.IsBool),
+ "security.syscalls.deny_compat": validate.Optional(validate.IsBool),
"security.syscalls.deny": validate.IsAny,
- "security.syscalls.intercept.mknod": validate.IsBool,
- "security.syscalls.intercept.mount": validate.IsBool,
+ "security.syscalls.intercept.mknod": validate.Optional(validate.IsBool),
+ "security.syscalls.intercept.mount": validate.Optional(validate.IsBool),
"security.syscalls.intercept.mount.allowed": validate.IsAny,
"security.syscalls.intercept.mount.fuse": validate.IsAny,
- "security.syscalls.intercept.mount.shift": validate.IsBool,
- "security.syscalls.intercept.setxattr": validate.IsBool,
+ "security.syscalls.intercept.mount.shift": validate.Optional(validate.IsBool),
+ "security.syscalls.intercept.setxattr": validate.Optional(validate.IsBool),
"security.syscalls.whitelist": validate.IsAny,
"snapshots.schedule": func(value string) error {
@@ -230,7 +230,7 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
return nil
},
- "snapshots.schedule.stopped": validate.IsBool,
+ "snapshots.schedule.stopped": validate.Optional(validate.IsBool),
"snapshots.pattern": validate.IsAny,
"snapshots.expiry": func(value string) error {
// Validate expression
From 377e61db55761f763a3589883cb7ac4fb51596f1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:02:55 +0100
Subject: [PATCH 11/33] shared/validate: Makes IsSize non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index cacdb38ab1..fb1075e423 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -113,10 +113,6 @@ func IsNotEmpty(value string) error {
// IsSize checks if string is valid size according to units.ParseByteSizeString.
func IsSize(value string) error {
- if value == "" {
- return nil
- }
-
_, err := units.ParseByteSizeString(value)
if err != nil {
return err
From ab374f269c961531ca6a722b4374b4a07de6301d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:03:46 +0100
Subject: [PATCH 12/33] lxd: Wraps validate.IsSize in validate.Optional
Now that validate.IsSize is non-optional.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/api_project.go | 4 ++--
lxd/storage/drivers/driver_lvm.go | 2 +-
lxd/storage/drivers/driver_lvm_volumes.go | 2 +-
lxd/storage/utils.go | 6 +++---
lxd/storage_pools_config.go | 6 +++---
5 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/lxd/api_project.go b/lxd/api_project.go
index c4541ac8c4..25b43c95c1 100644
--- a/lxd/api_project.go
+++ b/lxd/api_project.go
@@ -528,10 +528,10 @@ var projectConfigKeys = map[string]func(value string) error{
"features.storage.volumes": validate.Optional(validate.IsBool),
"limits.containers": validate.Optional(validate.IsUint32),
"limits.virtual-machines": validate.Optional(validate.IsUint32),
- "limits.memory": validate.IsSize,
+ "limits.memory": validate.Optional(validate.IsSize),
"limits.processes": validate.Optional(validate.IsUint32),
"limits.cpu": validate.Optional(validate.IsUint32),
- "limits.disk": validate.IsSize,
+ "limits.disk": validate.Optional(validate.IsSize),
"restricted": validate.Optional(validate.IsBool),
"restricted.containers.nesting": isEitherAllowOrBlock,
"restricted.containers.lowlevel": isEitherAllowOrBlock,
diff --git a/lxd/storage/drivers/driver_lvm.go b/lxd/storage/drivers/driver_lvm.go
index 5f25525ae9..2082cc8cdf 100644
--- a/lxd/storage/drivers/driver_lvm.go
+++ b/lxd/storage/drivers/driver_lvm.go
@@ -440,7 +440,7 @@ func (d *lvm) Validate(config map[string]string) error {
return validate.IsOneOf(value, lvmAllowedFilesystems)
},
"volume.lvm.stripes": validate.Optional(validate.IsUint32),
- "volume.lvm.stripes.size": validate.IsSize,
+ "volume.lvm.stripes.size": validate.Optional(validate.IsSize),
"lvm.vg.force_reuse": validate.Optional(validate.IsBool),
}
diff --git a/lxd/storage/drivers/driver_lvm_volumes.go b/lxd/storage/drivers/driver_lvm_volumes.go
index 2d9c730076..fbe1ef16da 100644
--- a/lxd/storage/drivers/driver_lvm_volumes.go
+++ b/lxd/storage/drivers/driver_lvm_volumes.go
@@ -249,7 +249,7 @@ func (d *lvm) ValidateVolume(vol Volume, removeUnknownKeys bool) error {
return validate.IsOneOf(value, lvmAllowedFilesystems)
},
"lvm.stripes": validate.Optional(validate.IsUint32),
- "lvm.stripes.size": validate.IsSize,
+ "lvm.stripes.size": validate.Optional(validate.IsSize),
}
err := d.validateVolume(vol, rules, removeUnknownKeys)
diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index 27be7d084b..19e8ba818a 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -421,8 +421,8 @@ func validatePoolCommonRules() map[string]func(string) error {
return map[string]func(string) error{
"source": validate.IsAny,
"volatile.initial_source": validate.IsAny,
- "volume.size": validate.IsSize,
- "size": validate.IsSize,
+ "volume.size": validate.Optional(validate.IsSize),
+ "size": validate.Optional(validate.IsSize),
"rsync.bwlimit": validate.IsAny,
}
}
@@ -435,7 +435,7 @@ func validateVolumeCommonRules(vol drivers.Volume) map[string]func(string) error
// Note: size should not be modifiable for non-custom volumes and should be checked
// in the relevant volume update functions.
- "size": validate.IsSize,
+ "size": validate.Optional(validate.IsSize),
"snapshots.expiry": func(value string) error {
// Validate expression
diff --git a/lxd/storage_pools_config.go b/lxd/storage_pools_config.go
index 2231de9985..cfcdf0b234 100644
--- a/lxd/storage_pools_config.go
+++ b/lxd/storage_pools_config.go
@@ -47,11 +47,11 @@ var storagePoolConfigKeys = map[string]func(value string) error{
"lvm.use_thinpool": validate.Optional(validate.IsBool),
"lvm.vg_name": validate.IsAny,
"volume.lvm.stripes": validate.Optional(validate.IsUint32),
- "volume.lvm.stripes.size": validate.IsSize,
+ "volume.lvm.stripes.size": validate.Optional(validate.IsSize),
"lvm.vg.force_reuse": validate.Optional(validate.IsBool),
// valid drivers: btrfs, lvm, zfs
- "size": validate.IsSize,
+ "size": validate.Optional(validate.IsSize),
// valid drivers: btrfs, dir, lvm, zfs
"source": validate.IsAny,
@@ -71,7 +71,7 @@ var storagePoolConfigKeys = map[string]func(value string) error{
"volume.block.mount_options": validate.IsAny,
// valid drivers: ceph, lvm
- "volume.size": validate.IsSize,
+ "volume.size": validate.Optional(validate.IsSize),
// valid drivers: zfs
"volume.zfs.remove_snapshots": validate.Optional(validate.IsBool),
From d1adf48d8ff7760048bcfdf933c8d0b59cb7d0af Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:04:10 +0100
Subject: [PATCH 13/33] shared/instance: Wraps validate.IsSize in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/instance.go | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/shared/instance.go b/shared/instance.go
index b596cbae6b..42eba631c1 100644
--- a/shared/instance.go
+++ b/shared/instance.go
@@ -137,10 +137,10 @@ var KnownInstanceConfigKeys = map[string]func(value string) error{
"limits.disk.priority": validate.Optional(validate.IsPriority),
- "limits.hugepages.64KB": validate.IsSize,
- "limits.hugepages.1MB": validate.IsSize,
- "limits.hugepages.2MB": validate.IsSize,
- "limits.hugepages.1GB": validate.IsSize,
+ "limits.hugepages.64KB": validate.Optional(validate.IsSize),
+ "limits.hugepages.1MB": validate.Optional(validate.IsSize),
+ "limits.hugepages.2MB": validate.Optional(validate.IsSize),
+ "limits.hugepages.1GB": validate.Optional(validate.IsSize),
"limits.memory": func(value string) error {
if value == "" {
From 8176f505dec5950f196d3024615c1d30ba4792bf Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:04:46 +0100
Subject: [PATCH 14/33] shared/validate: Makes IsNetworkAddress non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index fb1075e423..9820671356 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -153,10 +153,6 @@ func IsNetworkMAC(value string) error {
// IsNetworkAddress validates an IP (v4 or v6) address string. If string is empty, returns valid.
func IsNetworkAddress(value string) error {
- if value == "" {
- return nil
- }
-
ip := net.ParseIP(value)
if ip == nil {
return fmt.Errorf("Not an IP address %q", value)
From 4b9bc47c7c5f4a2a7ebed30a58ed20260118507a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:11:17 +0100
Subject: [PATCH 15/33] lxd: Wraps validate.IsNetworkAddress in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/device_utils_proxy.go | 2 +-
lxd/network/driver_bridge.go | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/lxd/device/device_utils_proxy.go b/lxd/device/device_utils_proxy.go
index 47455ffd94..6aae2a5b87 100644
--- a/lxd/device/device_utils_proxy.go
+++ b/lxd/device/device_utils_proxy.go
@@ -38,7 +38,7 @@ func ProxyParseAddr(addr string) (*deviceConfig.ProxyAddress, error) {
// Validate that it's a valid address.
if shared.StringInSlice(newProxyAddr.ConnType, []string{"udp", "tcp"}) {
- err := validate.IsNetworkAddress(address)
+ err := validate.Optional(validate.IsNetworkAddress)(address)
if err != nil {
return nil, err
}
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 0d466a3203..5c2e00c9fc 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -245,13 +245,13 @@ func (n *bridge) Validate(config map[string]string) error {
return validate.IsOneOf(value, []string{"gre", "vxlan"})
}
case "local":
- rules[k] = validate.IsNetworkAddress
+ rules[k] = validate.Optional(validate.IsNetworkAddress)
case "remote":
- rules[k] = validate.IsNetworkAddress
+ rules[k] = validate.Optional(validate.IsNetworkAddress)
case "port":
rules[k] = networkValidPort
case "group":
- rules[k] = validate.IsNetworkAddress
+ rules[k] = validate.Optional(validate.IsNetworkAddress)
case "id":
rules[k] = validate.Optional(validate.IsInt64)
case "inteface":
From ad0ab8ef7566506bcda34a44faccb5705997eaa2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:11:45 +0100
Subject: [PATCH 16/33] shared/validate: Makes IsNetworkV4 non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 9820671356..d21e748c7a 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -163,10 +163,6 @@ func IsNetworkAddress(value string) error {
// IsNetworkV4 validates an IPv4 CIDR string. If string is empty, returns valid.
func IsNetworkV4(value string) error {
- if value == "" {
- return nil
- }
-
ip, subnet, err := net.ParseCIDR(value)
if err != nil {
return err
From 3e56783e8b79e7d1e899b5abb04dd8e066976476 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:15:46 +0100
Subject: [PATCH 17/33] lxd/network/driver/bridge: Wraps validate.IsNetworkV4
in shared.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/driver_bridge.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 5c2e00c9fc..065faf50de 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -158,13 +158,13 @@ func (n *bridge) Validate(config map[string]string) error {
return validate.IsOneOf(value, []string{"standard", "fan"})
},
- "fan.overlay_subnet": validate.IsNetworkV4,
+ "fan.overlay_subnet": validate.Optional(validate.IsNetworkV4),
"fan.underlay_subnet": func(value string) error {
if value == "auto" {
return nil
}
- return validate.IsNetworkV4(value)
+ return validate.Optional(validate.IsNetworkV4)(value)
},
"fan.type": func(value string) error {
return validate.IsOneOf(value, []string{"vxlan", "ipip"})
From d0c933c0c2d44f1e554b227a46da3a1921c57168 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:16:26 +0100
Subject: [PATCH 18/33] shared/validate: Makes IsNetworkAddressV4 non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index d21e748c7a..a218eb7264 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -181,10 +181,6 @@ func IsNetworkV4(value string) error {
// IsNetworkAddressV4 validates an IPv4 addresss string. If string is empty, returns valid.
func IsNetworkAddressV4(value string) error {
- if value == "" {
- return nil
- }
-
ip := net.ParseIP(value)
if ip == nil || ip.To4() == nil {
return fmt.Errorf("Not an IPv4 address: %s", value)
From 081b4978f0ecd07bcec3dacad6a6560aadbee502 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:29:37 +0100
Subject: [PATCH 19/33] lxd/device/nic: Wraps validate.IsNetworkAddressV4 in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/device/nic.go b/lxd/device/nic.go
index 6e933bc443..5a15c3e4b1 100644
--- a/lxd/device/nic.go
+++ b/lxd/device/nic.go
@@ -23,14 +23,14 @@ func nicValidationRules(requiredFields []string, optionalFields []string) map[st
"security.ipv6_filtering": validate.IsAny,
"maas.subnet.ipv4": validate.IsAny,
"maas.subnet.ipv6": validate.IsAny,
- "ipv4.address": validate.IsNetworkAddressV4,
+ "ipv4.address": validate.Optional(validate.IsNetworkAddressV4),
"ipv6.address": validate.IsNetworkAddressV6,
"ipv4.routes": validate.IsNetworkV4List,
"ipv6.routes": validate.IsNetworkV6List,
"boot.priority": validate.Optional(validate.IsUint32),
"ipv4.gateway": networkValidGateway,
"ipv6.gateway": networkValidGateway,
- "ipv4.host_address": validate.IsNetworkAddressV4,
+ "ipv4.host_address": validate.Optional(validate.IsNetworkAddressV4),
"ipv6.host_address": validate.IsNetworkAddressV6,
"ipv4.host_table": validate.Optional(validate.IsUint32),
"ipv6.host_table": validate.Optional(validate.IsUint32),
From 80a844dcc933e2158d6aa2811b318fde0a8c142b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:30:13 +0100
Subject: [PATCH 20/33] lxd/device/nic/ipvlan: Wraps
validate.IsNetworkAddressV4 in validate.Optional
Removes duplicated empty check.
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_ipvlan.go | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index 26657dd667..9b25e904c8 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -117,11 +117,7 @@ func (d *nicIPVLAN) validateConfig(instConf instance.ConfigReader) error {
if d.config["mode"] == ipvlanModeL2 {
rules["ipv4.gateway"] = func(value string) error {
- if value == "" {
- return nil
- }
-
- return validate.IsNetworkAddressV4(value)
+ return validate.Optional(validate.IsNetworkAddressV4)(value)
}
rules["ipv6.gateway"] = func(value string) error {
From b0627a1ed6fb4e903d56a327cfe6863990f1a293 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:30:42 +0100
Subject: [PATCH 21/33] lxd/device/nic/ipvlan: Fixes incorrect IPv4 address
check in IPv6 context
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_ipvlan.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index 9b25e904c8..e21efcfed4 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -303,7 +303,7 @@ func (d *nicIPVLAN) Start() (*deviceConfig.RunConfig, error) {
addr = fmt.Sprintf("%s/128", addr)
}
- if mode == "l2" && validate.IsNetworkAddressV4(addr) == nil {
+ if mode == "l2" && validate.IsNetworkAddressV6(addr) == nil {
addr = fmt.Sprintf("%s/64", addr)
}
From c2222650f985d46f91301f52b380357d28c9df18 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:31:17 +0100
Subject: [PATCH 22/33] lxd/network/driver/bridge: Wraps
validate.IsNetworkAddressV4 in validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/network/driver_bridge.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 065faf50de..3dc8d2c317 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -182,9 +182,9 @@ func (n *bridge) Validate(config map[string]string) error {
"ipv4.nat.order": func(value string) error {
return validate.IsOneOf(value, []string{"before", "after"})
},
- "ipv4.nat.address": validate.IsNetworkAddressV4,
+ "ipv4.nat.address": validate.Optional(validate.IsNetworkAddressV4),
"ipv4.dhcp": validate.Optional(validate.IsBool),
- "ipv4.dhcp.gateway": validate.IsNetworkAddressV4,
+ "ipv4.dhcp.gateway": validate.Optional(validate.IsNetworkAddressV4),
"ipv4.dhcp.expiry": validate.IsAny,
"ipv4.dhcp.ranges": validate.IsAny,
"ipv4.routes": validate.IsNetworkV4List,
From 89d24cc7cfadd0d13b0bbc714456eb293b62ccf0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 12:32:08 +0100
Subject: [PATCH 23/33] shared/validate: Makes IsNetworkAddressCIDRV4
non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index a218eb7264..e4ce08cefa 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -191,10 +191,6 @@ func IsNetworkAddressV4(value string) error {
// IsNetworkAddressCIDRV4 validates an IPv4 addresss string in CIDR format. If string is empty, returns valid.
func IsNetworkAddressCIDRV4(value string) error {
- if value == "" {
- return nil
- }
-
ip, subnet, err := net.ParseCIDR(value)
if err != nil {
return err
From 1ede05b54b999aba32928be1c7b639f804081aeb Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:45:07 +0100
Subject: [PATCH 24/33] lxd: Wraps validate.IsNetworkAddressCIDRV4 in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/main_init_interactive.go | 2 +-
lxd/network/driver_bridge.go | 2 +-
lxd/network/driver_ovn.go | 778 +++++++++++++++++++++++++++++++++++
3 files changed, 780 insertions(+), 2 deletions(-)
create mode 100644 lxd/network/driver_ovn.go
diff --git a/lxd/main_init_interactive.go b/lxd/main_init_interactive.go
index cf548f264a..4414cb3241 100644
--- a/lxd/main_init_interactive.go
+++ b/lxd/main_init_interactive.go
@@ -371,7 +371,7 @@ func (c *cmdInit) askNetworking(config *cmdInitData, d lxd.InstanceServer) error
return nil
}
- return validate.IsNetworkAddressCIDRV4(value)
+ return validate.Optional(validate.IsNetworkAddressCIDRV4)(value)
})
if !shared.StringInSlice(net.Config["ipv4.address"], []string{"auto", "none"}) {
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 3dc8d2c317..1cb7773279 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -175,7 +175,7 @@ func (n *bridge) Validate(config map[string]string) error {
return nil
}
- return validate.IsNetworkAddressCIDRV4(value)
+ return validate.Optional(validate.IsNetworkAddressCIDRV4)(value)
},
"ipv4.firewall": validate.Optional(validate.IsBool),
"ipv4.nat": validate.Optional(validate.IsBool),
diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
new file mode 100644
index 0000000000..2da3430e90
--- /dev/null
+++ b/lxd/network/driver_ovn.go
@@ -0,0 +1,778 @@
+package network
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net"
+ "strings"
+ "time"
+
+ "github.com/pkg/errors"
+
+ "github.com/lxc/lxd/lxd/dnsmasq/dhcpalloc"
+ "github.com/lxc/lxd/lxd/instance"
+ "github.com/lxc/lxd/lxd/network/openvswitch"
+ "github.com/lxc/lxd/lxd/project"
+ "github.com/lxc/lxd/lxd/revert"
+ "github.com/lxc/lxd/shared/api"
+ log "github.com/lxc/lxd/shared/log15"
+ "github.com/lxc/lxd/shared/validate"
+)
+
+//ovnVars OVN object variables.
+type ovnVars struct {
+ // OVN client.
+ client *openvswitch.OVN
+
+ // Chassis.
+ chassisGroupName openvswitch.OVNChassisGroup
+
+ // Router.
+ routerName openvswitch.OVNRouter
+ routerExtPortName openvswitch.OVNRouterPort
+ routerIntPortName openvswitch.OVNRouterPort
+ routerExtPortIPv4Net string
+ routerExtPortIPv6Net string
+ routerIntPortIPv4Net string
+ routerIntPortIPv6Net string
+ routerExtGwIPv4 string
+ routerExtGwIPv6 string
+ routerMAC net.HardwareAddr
+
+ // External Switch.
+ extSwitchName openvswitch.OVNSwitch
+ extSwitchRouterPortName openvswitch.OVNSwitchPort
+ extSwitchProviderPortName openvswitch.OVNSwitchPort
+ extSwitchProviderName string
+
+ // Internal Switch.
+ intSwitchName openvswitch.OVNSwitch
+ intSwitchRouterPortName openvswitch.OVNSwitchPort
+ intSwitchInstancePortPrefix string
+
+ // DNS.
+ domainName string
+ dnsSearchList []string
+ dnsIPv6 string
+ dnsIPv4 string
+}
+
+// ovn represents a LXD OVN network.
+type ovn struct {
+ common
+}
+
+// getOVNVars returns OVN object variables generated from current config.
+func (n *ovn) getOVNVars() (*ovnVars, error) {
+ var err error
+ v := ovnVars{}
+
+ v.client = openvswitch.NewOVN()
+ v.client.SetDatabaseAddress("tcp:10.109.89.169:6643") // tomp TODO we need to get this out of host OVS or LXD daemon config.
+
+ // Initialise network object names.
+ netName := fmt.Sprintf("lxd-net%d", n.id)
+
+ // Chassis group.
+ v.chassisGroupName = openvswitch.OVNChassisGroup(netName)
+
+ // Router network objects (for connecting to both external and internal switches).
+ v.routerName = openvswitch.OVNRouter(fmt.Sprintf("%s-lr", netName))
+ v.routerExtPortName = openvswitch.OVNRouterPort(fmt.Sprintf("%s-lrp-ext", v.routerName))
+ v.routerIntPortName = openvswitch.OVNRouterPort(fmt.Sprintf("%s-lrp-int", v.routerName))
+ routerMAC := n.config["bridge.hwaddr"]
+ if routerMAC == "" {
+ routerMAC = n.config["volatile.bridge.hwaddr"]
+ }
+
+ v.routerMAC, err = net.ParseMAC(routerMAC)
+ if err != nil {
+ return nil, errors.Wrapf(err, "Failed parsing router MAC address %q", routerMAC)
+ }
+
+ // Load parent network.
+ parentNet, err := LoadByName(n.state, n.config["parent"])
+ if err != nil {
+ return nil, errors.Wrapf(err, "Failead loading parent network")
+ }
+ parentNetConf := parentNet.Config()
+
+ // Parent derived settings.
+ v.extSwitchProviderName = parentNet.Name()
+
+ parentIPv4, parentIPv4Mask, err := net.ParseCIDR(parentNetConf["ipv4.address"])
+ if err == nil {
+ v.dnsIPv4 = parentIPv4.String()
+ v.routerExtGwIPv4 = parentIPv4.String()
+ }
+
+ parentIPv6, parentIPv6Mask, err := net.ParseCIDR(parentNetConf["ipv6.address"])
+ if err == nil {
+ v.dnsIPv6 = parentIPv6.String()
+ v.routerExtGwIPv6 = parentIPv6.String()
+ }
+
+ // Allocate (or retrieve existing allocation) for the router's external IP on the parent network.
+ opts := &dhcpalloc.Options{
+ ProjectName: project.Default,
+ HostName: netName,
+ HostMAC: v.routerMAC,
+ Network: parentNet,
+ }
+
+ err = dhcpalloc.AllocateTask(opts, func(t *dhcpalloc.Transaction) error {
+ if parentIPv4 != nil {
+ routerExtPortIPv4, err := t.AllocateIPv4()
+
+ // If DHCP not supported, skip error, and will result in total protocol filter.
+ if err != nil && err != dhcpalloc.ErrDHCPNotSupported {
+ return err
+ }
+
+ routerExtPortIPv4Net := &net.IPNet{
+ Mask: parentIPv4Mask.Mask,
+ IP: routerExtPortIPv4,
+ }
+ v.routerExtPortIPv4Net = routerExtPortIPv4Net.String()
+ }
+
+ if parentIPv6 != nil {
+ routerExtPortIPv6, err := t.AllocateIPv6()
+
+ // If DHCP not supported, skip error, and will result in total protocol filter.
+ if err != nil && err != dhcpalloc.ErrDHCPNotSupported {
+ return err
+ }
+
+ routerExtPortIPv6Net := &net.IPNet{
+ Mask: parentIPv6Mask.Mask,
+ IP: routerExtPortIPv6,
+ }
+ v.routerExtPortIPv6Net = routerExtPortIPv6Net.String()
+ }
+
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ // Router settings.
+ v.routerIntPortIPv4Net = n.config["ipv4.address"]
+ v.routerIntPortIPv6Net = n.config["ipv6.address"]
+
+ // Domain settings.
+ v.domainName = "lxd"
+ if n.config["dns.domain"] != "" {
+ v.domainName = n.config["dns.domain"]
+ }
+
+ v.dnsSearchList = []string{"dns.domain"}
+ if n.config["dns.search"] != "" {
+ v.dnsSearchList = []string{}
+ for _, domain := range strings.SplitN(n.config["dns.search"], ",", -1) {
+ v.dnsSearchList = append(v.dnsSearchList, strings.TrimSpace(domain))
+ }
+ }
+
+ // External network objects (for connecting logical router and external network).
+ v.extSwitchName = openvswitch.OVNSwitch(fmt.Sprintf("%s-ls-ext", netName))
+ v.extSwitchRouterPortName = openvswitch.OVNSwitchPort(fmt.Sprintf("%s-lsp-router", v.extSwitchName))
+ v.extSwitchProviderPortName = openvswitch.OVNSwitchPort(fmt.Sprintf("%s-lsp-provider", v.extSwitchName))
+
+ // Internal network objects (for connecting logical router and instance NICs).
+ v.intSwitchName = openvswitch.OVNSwitch(fmt.Sprintf("%s-ls-int", netName))
+ v.intSwitchRouterPortName = openvswitch.OVNSwitchPort(fmt.Sprintf("%s-lsp-router", v.intSwitchName))
+ v.intSwitchInstancePortPrefix = fmt.Sprintf("%s-instance", netName)
+
+ return &v, nil
+}
+
+// Validate network config.
+func (n *ovn) Validate(config map[string]string) error {
+ rules := map[string]func(value string) error{
+ "parent": func(value string) error {
+ if err := ValidNetworkName(value); err != nil {
+ return errors.Wrapf(err, "Invalid network name %q", value)
+ }
+
+ return nil
+ },
+ "maas.subnet.ipv4": validate.IsAny,
+ "maas.subnet.ipv6": validate.IsAny,
+ "bridge.hwaddr": func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ return validate.IsNetworkMAC(value)
+ },
+ "volatile.bridge.hwaddr": func(value string) error {
+ if value == "" {
+ return nil
+ }
+
+ return validate.IsNetworkMAC(value)
+ },
+ "ipv4.address": func(value string) error {
+ if validate.IsOneOf(value, []string{"auto"}) == nil {
+ return nil
+ }
+
+ return validate.Optional(validate.IsNetworkAddressCIDRV4)(value)
+ },
+ "ipv6.address": func(value string) error {
+ if validate.IsOneOf(value, []string{"auto"}) == nil {
+ return nil
+ }
+
+ return validate.IsNetworkAddressCIDRV6(value)
+ },
+ "dns.domain": validate.IsAny,
+ "dns.search": validate.IsAny,
+ }
+
+ err := n.validate(config, rules)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// fillConfig fills requested config with any default values.
+func (n *ovn) fillConfig(config map[string]string) error {
+ if config["ipv4.address"] == "" {
+ config["ipv4.address"] = "auto"
+ }
+
+ if config["ipv6.address"] == "" {
+ content, err := ioutil.ReadFile("/proc/sys/net/ipv6/conf/default/disable_ipv6")
+ if err == nil && string(content) == "0\n" {
+ config["ipv6.address"] = "auto"
+ }
+ }
+
+ // If no static hwaddr specified generate a volatile one to store in DB record so that
+ // there are no races when starting the network at the same time on multiple cluster nodes.
+ err := n.fillHwaddr(config)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// fillHwaddr populates the volatile.bridge.hwaddr in config if it, nor bridge.hwaddr, are already set.
+func (n *ovn) fillHwaddr(config map[string]string) error {
+ if config["bridge.hwaddr"] != "" || config["volatile.bridge.hwaddr"] != "" {
+ return nil
+ }
+
+ // If no existing MAC address, generate a new one and store in volatile.
+ hwAddr, err := instance.DeviceNextInterfaceHWAddr()
+ if err != nil {
+ return errors.Wrapf(err, "Failed generating MAC address")
+ }
+
+ config["volatile.bridge.hwaddr"] = hwAddr
+ return nil
+}
+
+// Create sets up network in OVN Northbound database.
+func (n *ovn) Create(clusterNotification bool) error {
+ n.logger.Debug("Create", log.Ctx{"clusterNotification": clusterNotification, "config": n.config})
+
+ // We only need to setup the OVN Northbound database once, not on every clustered node.
+ if !clusterNotification {
+ err := n.setup(false)
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (n *ovn) setup(update bool) error {
+ // If we are in mock mode, just no-op.
+ if n.state.OS.MockMode {
+ return nil
+ }
+
+ n.logger.Debug("Setting up network")
+
+ revert := revert.New()
+ defer revert.Fail()
+
+ var routerExtPortIPv4, routerIntPortIPv4, routerExtPortIPv6, routerIntPortIPv6 net.IP
+ var routerExtPortIPv4Net, routerIntPortIPv4Net, routerExtPortIPv6Net, routerIntPortIPv6Net *net.IPNet
+
+ ovn, err := n.getOVNVars()
+ if err != nil {
+ return err
+ }
+
+ // Parse router IP config.
+ if ovn.routerExtPortIPv4Net != "" {
+ routerExtPortIPv4, routerExtPortIPv4Net, err = net.ParseCIDR(ovn.routerExtPortIPv4Net)
+ if err != nil {
+ return err
+ }
+ }
+
+ if ovn.routerExtPortIPv6Net != "" {
+ routerExtPortIPv6, routerExtPortIPv6Net, err = net.ParseCIDR(ovn.routerExtPortIPv6Net)
+ if err != nil {
+ return err
+ }
+ }
+
+ if ovn.routerIntPortIPv4Net != "" {
+ routerIntPortIPv4, routerIntPortIPv4Net, err = net.ParseCIDR(ovn.routerIntPortIPv4Net)
+ if err != nil {
+ return err
+ }
+ }
+
+ if ovn.routerIntPortIPv6Net != "" {
+ routerIntPortIPv6, routerIntPortIPv6Net, err = net.ParseCIDR(ovn.routerIntPortIPv6Net)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Create chassis group.
+ err = ovn.client.ChassisGroupAdd(ovn.chassisGroupName, update)
+ if err != nil {
+ return err
+ }
+
+ revert.Add(func() { ovn.client.ChassisGroupDelete(ovn.chassisGroupName) })
+
+ // Create logical router.
+ if update {
+ ovn.client.LogicalRouterDelete(ovn.routerName)
+ }
+
+ err = ovn.client.LogicalRouterAdd(ovn.routerName)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding router")
+ }
+ revert.Add(func() { ovn.client.LogicalRouterDelete(ovn.routerName) })
+
+ // Configure logical router.
+
+ // Add default routes.
+ if ovn.routerExtGwIPv4 != "" {
+ err = ovn.client.LogicalRouterRouteAdd(ovn.routerName, &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, net.ParseIP(ovn.routerExtGwIPv4))
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding IPv4 default route")
+ }
+ }
+
+ if ovn.routerExtGwIPv6 != "" {
+ err = ovn.client.LogicalRouterRouteAdd(ovn.routerName, &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, net.ParseIP(ovn.routerExtGwIPv6))
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding IPv6 default route")
+ }
+ }
+
+ // Add SNAT rules.
+ if routerIntPortIPv4Net != nil {
+ err = ovn.client.LogicalRouterSNATAdd(ovn.routerName, routerIntPortIPv4Net, routerExtPortIPv4)
+ if err != nil {
+ return err
+ }
+ }
+
+ if routerIntPortIPv6Net != nil {
+ err = ovn.client.LogicalRouterSNATAdd(ovn.routerName, routerIntPortIPv6Net, routerExtPortIPv6)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Create external logical switch.
+ if update {
+ ovn.client.LogicalSwitchDelete(ovn.extSwitchName)
+ }
+
+ err = ovn.client.LogicalSwitchAdd(ovn.extSwitchName, false)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding external switch")
+ }
+ revert.Add(func() { ovn.client.LogicalSwitchDelete(ovn.extSwitchName) })
+
+ // Generate external router port IPs (in CIDR format).
+ extRouterIPs := []*net.IPNet{}
+ if routerExtPortIPv4Net != nil {
+ extRouterIPs = append(extRouterIPs, &net.IPNet{
+ IP: routerExtPortIPv4,
+ Mask: routerExtPortIPv4Net.Mask,
+ })
+ }
+
+ if routerExtPortIPv6Net != nil {
+ extRouterIPs = append(extRouterIPs, &net.IPNet{
+ IP: routerExtPortIPv6,
+ Mask: routerExtPortIPv6Net.Mask,
+ })
+ }
+
+ // Create external router port.
+ err = ovn.client.LogicalRouterPortAdd(ovn.routerName, ovn.routerExtPortName, ovn.routerMAC, extRouterIPs...)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding external router port")
+ }
+ revert.Add(func() { ovn.client.LogicalRouterPortDelete(ovn.routerExtPortName) })
+
+ // Create external switch port and link to router port.
+ err = ovn.client.LogicalSwitchPortAdd(ovn.extSwitchName, ovn.extSwitchRouterPortName, false)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding external switch router port")
+ }
+ revert.Add(func() { ovn.client.LogicalSwitchPortDelete(ovn.extSwitchRouterPortName) })
+
+ err = ovn.client.LogicalSwitchPortLinkRouter(ovn.extSwitchRouterPortName, ovn.routerExtPortName)
+ if err != nil {
+ return errors.Wrapf(err, "Failed linking external router port to external switch port")
+ }
+
+ // Create external switch port and link to external provider network.
+ err = ovn.client.LogicalSwitchPortAdd(ovn.extSwitchName, ovn.extSwitchProviderPortName, false)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding external switch provider port")
+ }
+ revert.Add(func() { ovn.client.LogicalSwitchPortDelete(ovn.extSwitchProviderPortName) })
+
+ err = ovn.client.LogicalSwitchPortLinkProviderNetwork(ovn.extSwitchProviderPortName, ovn.extSwitchProviderName)
+ if err != nil {
+ return errors.Wrapf(err, "Failed linking external switch provider port to external provider network")
+ }
+
+ // Create internal logical switch if not updating.
+ err = ovn.client.LogicalSwitchAdd(ovn.intSwitchName, update)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding internal switch")
+ }
+ revert.Add(func() { ovn.client.LogicalSwitchDelete(ovn.intSwitchName) })
+
+ // Setup IP allocation config on logical switch.
+ err = ovn.client.LogicalSwitchSetIPAllocation(ovn.intSwitchName, &openvswitch.OVNIPAllocationOpts{
+ PrefixIPv4: routerIntPortIPv4Net,
+ PrefixIPv6: routerIntPortIPv6Net,
+ ExcludeIPv4: []openvswitch.OVNIPRange{{Start: routerIntPortIPv4}},
+ })
+ if err != nil {
+ return errors.Wrapf(err, "Failed setting IP allocation settings on internal switch")
+ }
+
+ // Find MAC address for internal router port.
+
+ var dhcpv4UUID, dhcpv6UUID string
+
+ if update {
+ // Find first existing DHCP options set for IPv4 and IPv6 and update them instead of adding sets.
+ existingOpts, err := ovn.client.LogicalSwitchDHCPOptionsGet(ovn.intSwitchName)
+ if err != nil {
+ return errors.Wrapf(err, "Failed getting existing DHCP settings for internal switch")
+ }
+
+ for _, existingOpt := range existingOpts {
+ if existingOpt.CIDR.IP.To4() == nil {
+ if dhcpv6UUID == "" {
+ dhcpv6UUID = existingOpt.UUID
+ }
+ } else {
+ if dhcpv4UUID == "" {
+ dhcpv4UUID = existingOpt.UUID
+ }
+ }
+ }
+ }
+
+ // Create DHCPv4 options for internal switch.
+ err = ovn.client.LogicalSwitchDHCPv4OptionsSet(ovn.intSwitchName, dhcpv4UUID, routerIntPortIPv4Net, &openvswitch.OVNDHCPv4Opts{
+ ServerID: routerIntPortIPv4,
+ ServerMAC: ovn.routerMAC,
+ Router: routerIntPortIPv4,
+ RecursiveDNSServer: net.ParseIP(ovn.dnsIPv4),
+ DomainName: ovn.domainName,
+ LeaseTime: time.Duration(time.Hour * 1),
+ })
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding DHCPv4 settings for internal switch")
+ }
+
+ // Create DHCPv6 options for internal switch.
+ err = ovn.client.LogicalSwitchDHCPv6OptionsSet(ovn.intSwitchName, dhcpv6UUID, routerIntPortIPv6Net, &openvswitch.OVNDHCPv6Opts{
+ ServerID: ovn.routerMAC,
+ RecursiveDNSServer: net.ParseIP(ovn.dnsIPv6),
+ DNSSearchList: ovn.dnsSearchList,
+ })
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding DHCPv6 settings for internal switch")
+ }
+
+ // Generate internal router port IPs (in CIDR format).
+ intRouterIPs := []*net.IPNet{}
+ if routerIntPortIPv4Net != nil {
+ intRouterIPs = append(intRouterIPs, &net.IPNet{
+ IP: routerIntPortIPv4,
+ Mask: routerIntPortIPv4Net.Mask,
+ })
+ }
+
+ if routerIntPortIPv6Net != nil {
+ intRouterIPs = append(intRouterIPs, &net.IPNet{
+ IP: routerIntPortIPv6,
+ Mask: routerIntPortIPv6Net.Mask,
+ })
+ }
+
+ // Create internal router port.
+ err = ovn.client.LogicalRouterPortAdd(ovn.routerName, ovn.routerIntPortName, ovn.routerMAC, intRouterIPs...)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding internal router port")
+ }
+ revert.Add(func() { ovn.client.LogicalRouterPortDelete(ovn.routerIntPortName) })
+
+ // Set IPv6 router advertisement settings.
+ if routerIntPortIPv6Net != nil {
+ err = ovn.client.LogicalRouterPortSetIPv6Advertisements(ovn.routerIntPortName, &openvswitch.OVNIPv6RAOpts{
+ AddressMode: openvswitch.OVNIPv6AddressModeSLAAC,
+ SendPeriodic: true,
+ DNSSearchList: ovn.dnsSearchList,
+ RecursiveDNSServer: net.ParseIP(ovn.dnsIPv6),
+
+ // Keep these low until we support DNS search domains via DHCPv4, as otherwise RA DNSSL
+ // won't take effect until advert after DHCPv4 has run on instance.
+ MinInterval: time.Duration(time.Second * 30),
+ MaxInterval: time.Duration(time.Minute * 1),
+ })
+ if err != nil {
+ return errors.Wrapf(err, "Failed setting internal router port IPv6 advertisement settings")
+ }
+ }
+
+ // Create internal switch port and link to router port.
+ err = ovn.client.LogicalSwitchPortAdd(ovn.intSwitchName, ovn.intSwitchRouterPortName, update)
+ if err != nil {
+ return errors.Wrapf(err, "Failed adding internal switch router port")
+ }
+ revert.Add(func() { ovn.client.LogicalSwitchPortDelete(ovn.intSwitchRouterPortName) })
+
+ err = ovn.client.LogicalSwitchPortLinkRouter(ovn.intSwitchRouterPortName, ovn.routerIntPortName)
+ if err != nil {
+ return errors.Wrapf(err, "Failed linking internal router port to internal switch port")
+ }
+
+ revert.Success()
+ return nil
+}
+
+// Delete deletes a network.
+func (n *ovn) Delete(clusterNotification bool) error {
+ n.logger.Debug("Delete", log.Ctx{"clusterNotification": clusterNotification})
+
+ if !clusterNotification {
+ ovn, err := n.getOVNVars()
+ if err != nil {
+ return err
+ }
+
+ ovn.client.ChassisGroupDelete(ovn.chassisGroupName)
+ ovn.client.LogicalRouterDelete(ovn.routerName)
+ ovn.client.LogicalSwitchDelete(ovn.extSwitchName)
+ ovn.client.LogicalSwitchDelete(ovn.intSwitchName)
+ ovn.client.LogicalRouterPortDelete(ovn.routerExtPortName)
+ ovn.client.LogicalRouterPortDelete(ovn.routerIntPortName)
+ ovn.client.LogicalSwitchPortDelete(ovn.extSwitchRouterPortName)
+ ovn.client.LogicalSwitchPortDelete(ovn.extSwitchProviderPortName)
+ ovn.client.LogicalSwitchPortDelete(ovn.intSwitchRouterPortName)
+ }
+
+ return n.common.delete(clusterNotification)
+}
+
+// Rename renames a network.
+func (n *ovn) Rename(newName string) error {
+ n.logger.Debug("Rename", log.Ctx{"newName": newName})
+
+ // Sanity checks.
+ inUse, err := n.IsUsed()
+ if err != nil {
+ return err
+ }
+
+ if inUse {
+ return fmt.Errorf("The network is currently in use")
+ }
+
+ // Rename common steps.
+ err = n.common.rename(newName)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Start starts is a no-op.
+func (n *ovn) Start() error {
+ if n.status == api.NetworkStatusPending {
+ return fmt.Errorf("Cannot start pending network")
+ }
+
+ return nil
+}
+
+// Stop stops is a no-op.
+func (n *ovn) Stop() error {
+ return nil
+}
+
+// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
+// cluster notification, in which case do not update the database, just apply local changes needed.
+func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, clusterNotification bool) error {
+ n.logger.Debug("Update", log.Ctx{"clusterNotification": clusterNotification, "newNetwork": newNetwork})
+
+ // Populate default values if they are missing.
+ err := n.fillConfig(newNetwork.Config)
+ if err != nil {
+ return err
+ }
+
+ // Populate auto fields.
+ err = fillAuto(newNetwork.Config)
+ if err != nil {
+ return err
+ }
+
+ dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
+ if err != nil {
+ return err
+ }
+
+ if !dbUpdateNeeeded {
+ return nil // Nothing changed.
+ }
+
+ revert := revert.New()
+ defer revert.Fail()
+
+ // Define a function which reverts everything.
+ revert.Add(func() {
+ // Reset changes to all nodes and database.
+ n.common.update(oldNetwork, targetNode, clusterNotification)
+
+ // Reset any change that was made to network.
+ if !clusterNotification {
+ n.setup(true)
+ }
+ })
+
+ // Apply changes to database.
+ err = n.common.update(newNetwork, targetNode, clusterNotification)
+ if err != nil {
+ return err
+ }
+
+ // Restart the network if needed.
+ if len(changedKeys) > 0 && !clusterNotification {
+ err = n.setup(true)
+ if err != nil {
+ return err
+ }
+ }
+
+ revert.Success()
+ return nil
+}
+
+// getInstanceDevicePortName returns the switch port name to use for an instance device.
+func (n *ovn) getInstanceDevicePortName(v *ovnVars, instanceID int, deviceName string) openvswitch.OVNSwitchPort {
+ return openvswitch.OVNSwitchPort(fmt.Sprintf("%s-%d-%s", v.intSwitchInstancePortPrefix, instanceID, deviceName))
+}
+
+// instanceDevicePortAdd adds an instance device port to the internal logical switch and returns the port name.
+func (n *ovn) instanceDevicePortAdd(instanceID int, deviceName string, mac net.HardwareAddr, ips []net.IP) (openvswitch.OVNSwitchPort, error) {
+ var dhcpV4ID, dhcpv6ID string
+
+ revert := revert.New()
+ defer revert.Fail()
+
+ ovn, err := n.getOVNVars()
+ if err != nil {
+ return "", err
+ }
+
+ // Get DHCP options IDs.
+ if ovn.routerIntPortIPv4Net != "" {
+ _, routerIntPortIPv4Net, err := net.ParseCIDR(ovn.routerIntPortIPv4Net)
+ if err != nil {
+ return "", err
+ }
+
+ dhcpV4ID, err = ovn.client.LogicalSwitchDHCPOptionsGetID(ovn.intSwitchName, routerIntPortIPv4Net)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ if ovn.routerIntPortIPv6Net != "" {
+ _, routerIntPortIPv6Net, err := net.ParseCIDR(ovn.routerIntPortIPv6Net)
+ if err != nil {
+ return "", err
+ }
+
+ dhcpv6ID, err = ovn.client.LogicalSwitchDHCPOptionsGetID(ovn.intSwitchName, routerIntPortIPv6Net)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ instancePortName := n.getInstanceDevicePortName(ovn, instanceID, deviceName)
+
+ // Add port with mayExist set to true, so that if instance port exists, we don't fail and continue below
+ // to configure the port as needed. This is required in case the OVN northbound database was unavailable
+ // when the instance NIC was stopped and was unable to remove the port on last stop, which would otherwise
+ // prevent future NIC starts.
+ err = ovn.client.LogicalSwitchPortAdd(ovn.intSwitchName, instancePortName, true)
+ if err != nil {
+ return "", err
+ }
+
+ revert.Add(func() { ovn.client.LogicalSwitchPortDelete(instancePortName) })
+
+ err = ovn.client.LogicalSwitchPortSet(instancePortName, &openvswitch.OVNSwitchPortOpts{
+ DHCPv4OptsID: dhcpV4ID,
+ DHCPv6OptsID: dhcpv6ID,
+ MAC: mac,
+ IPs: ips,
+ })
+ if err != nil {
+ return "", err
+ }
+
+ revert.Success()
+ return instancePortName, nil
+}
+
+// instanceDevicePortDelete deletes an instance device port from the internal logical switch.
+func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string) error {
+ ovn, err := n.getOVNVars()
+ if err != nil {
+ return err
+ }
+
+ instancePortName := n.getInstanceDevicePortName(ovn, instanceID, deviceName)
+
+ err = ovn.client.LogicalSwitchPortDelete(instancePortName)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
From 4e3017ca427543389c673d55b943a78868680b0b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:46:15 +0100
Subject: [PATCH 25/33] shared/validate: Makes IsDeviceID non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index e4ce08cefa..3c590f37da 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -123,10 +123,6 @@ func IsSize(value string) error {
// IsDeviceID validates string is four lowercase hex characters suitable as Vendor or Device ID.
func IsDeviceID(value string) error {
- if value == "" {
- return nil
- }
-
regexHexLc, err := regexp.Compile("^[0-9a-f]+$")
if err != nil {
return err
From 39d171809d37de54527588d4b990adde7a85502c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:46:59 +0100
Subject: [PATCH 26/33] lxd/device: Wraps validate.IsDeviceID in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/gpu.go | 4 ++--
lxd/device/unix_hotplug.go | 4 ++--
lxd/device/usb.go | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/lxd/device/gpu.go b/lxd/device/gpu.go
index 420ff0f450..9c7724077b 100644
--- a/lxd/device/gpu.go
+++ b/lxd/device/gpu.go
@@ -40,8 +40,8 @@ func (d *gpu) validateConfig(instConf instance.ConfigReader) error {
}
rules := map[string]func(string) error{
- "vendorid": validate.IsDeviceID,
- "productid": validate.IsDeviceID,
+ "vendorid": validate.Optional(validate.IsDeviceID),
+ "productid": validate.Optional(validate.IsDeviceID),
"id": validate.IsAny,
"pci": validate.IsAny,
"uid": unixValidUserID,
diff --git a/lxd/device/unix_hotplug.go b/lxd/device/unix_hotplug.go
index 71f62b6f0b..6f3f39370a 100644
--- a/lxd/device/unix_hotplug.go
+++ b/lxd/device/unix_hotplug.go
@@ -48,8 +48,8 @@ func (d *unixHotplug) validateConfig(instConf instance.ConfigReader) error {
}
rules := map[string]func(string) error{
- "vendorid": validate.IsDeviceID,
- "productid": validate.IsDeviceID,
+ "vendorid": validate.Optional(validate.IsDeviceID),
+ "productid": validate.Optional(validate.IsDeviceID),
"uid": unixValidUserID,
"gid": unixValidUserID,
"mode": unixValidOctalFileMode,
diff --git a/lxd/device/usb.go b/lxd/device/usb.go
index 155e62cec6..6e5c09aab9 100644
--- a/lxd/device/usb.go
+++ b/lxd/device/usb.go
@@ -50,8 +50,8 @@ func (d *usb) validateConfig(instConf instance.ConfigReader) error {
}
rules := map[string]func(string) error{
- "vendorid": validate.IsDeviceID,
- "productid": validate.IsDeviceID,
+ "vendorid": validate.Optional(validate.IsDeviceID),
+ "productid": validate.Optional(validate.IsDeviceID),
"uid": unixValidUserID,
"gid": unixValidUserID,
"mode": unixValidOctalFileMode,
From 3aaf8dbbca9b5c57933acd30d74e7d6ba6229515 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:47:28 +0100
Subject: [PATCH 27/33] shared/validate: Makes IsNetworkV6 non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 3c590f37da..f7b7da53d2 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -230,10 +230,6 @@ func IsNetworkV4List(value string) error {
// IsNetworkV6 validates an IPv6 CIDR string. If string is empty, returns valid.
func IsNetworkV6(value string) error {
- if value == "" {
- return nil
- }
-
ip, subnet, err := net.ParseCIDR(value)
if err != nil {
return err
From 12af7aada96ec58583f4abd086f1474607b46211 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:49:52 +0100
Subject: [PATCH 28/33] shared/validate: Makes IsNetworkAddressCIDRV6
non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index f7b7da53d2..0856e89f0d 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -262,10 +262,6 @@ func IsNetworkAddressV6(value string) error {
// IsNetworkAddressCIDRV6 validates an IPv6 addresss string in CIDR format. If string is empty, returns valid.
func IsNetworkAddressCIDRV6(value string) error {
- if value == "" {
- return nil
- }
-
ip, subnet, err := net.ParseCIDR(value)
if err != nil {
return err
From 75463c23387172cd03fc9a9361cca7d364f7d358 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:50:55 +0100
Subject: [PATCH 29/33] lxd: Wraps validate.IsNetworkAddressCIDRV6 in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/main_init_interactive.go | 2 +-
lxd/network/driver_bridge.go | 2 +-
lxd/network/driver_ovn.go | 778 -----------------------------------
3 files changed, 2 insertions(+), 780 deletions(-)
delete mode 100644 lxd/network/driver_ovn.go
diff --git a/lxd/main_init_interactive.go b/lxd/main_init_interactive.go
index 4414cb3241..7a6bacdf5a 100644
--- a/lxd/main_init_interactive.go
+++ b/lxd/main_init_interactive.go
@@ -385,7 +385,7 @@ func (c *cmdInit) askNetworking(config *cmdInitData, d lxd.InstanceServer) error
return nil
}
- return validate.IsNetworkAddressCIDRV6(value)
+ return validate.Optional(validate.IsNetworkAddressCIDRV6)(value)
})
if !shared.StringInSlice(net.Config["ipv6.address"], []string{"auto", "none"}) {
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index 1cb7773279..d96a13acfd 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -195,7 +195,7 @@ func (n *bridge) Validate(config map[string]string) error {
return nil
}
- return validate.IsNetworkAddressCIDRV6(value)
+ return validate.Optional(validate.IsNetworkAddressCIDRV6)(value)
},
"ipv6.firewall": validate.Optional(validate.IsBool),
"ipv6.nat": validate.Optional(validate.IsBool),
diff --git a/lxd/network/driver_ovn.go b/lxd/network/driver_ovn.go
deleted file mode 100644
index 2da3430e90..0000000000
--- a/lxd/network/driver_ovn.go
+++ /dev/null
@@ -1,778 +0,0 @@
-package network
-
-import (
- "fmt"
- "io/ioutil"
- "net"
- "strings"
- "time"
-
- "github.com/pkg/errors"
-
- "github.com/lxc/lxd/lxd/dnsmasq/dhcpalloc"
- "github.com/lxc/lxd/lxd/instance"
- "github.com/lxc/lxd/lxd/network/openvswitch"
- "github.com/lxc/lxd/lxd/project"
- "github.com/lxc/lxd/lxd/revert"
- "github.com/lxc/lxd/shared/api"
- log "github.com/lxc/lxd/shared/log15"
- "github.com/lxc/lxd/shared/validate"
-)
-
-//ovnVars OVN object variables.
-type ovnVars struct {
- // OVN client.
- client *openvswitch.OVN
-
- // Chassis.
- chassisGroupName openvswitch.OVNChassisGroup
-
- // Router.
- routerName openvswitch.OVNRouter
- routerExtPortName openvswitch.OVNRouterPort
- routerIntPortName openvswitch.OVNRouterPort
- routerExtPortIPv4Net string
- routerExtPortIPv6Net string
- routerIntPortIPv4Net string
- routerIntPortIPv6Net string
- routerExtGwIPv4 string
- routerExtGwIPv6 string
- routerMAC net.HardwareAddr
-
- // External Switch.
- extSwitchName openvswitch.OVNSwitch
- extSwitchRouterPortName openvswitch.OVNSwitchPort
- extSwitchProviderPortName openvswitch.OVNSwitchPort
- extSwitchProviderName string
-
- // Internal Switch.
- intSwitchName openvswitch.OVNSwitch
- intSwitchRouterPortName openvswitch.OVNSwitchPort
- intSwitchInstancePortPrefix string
-
- // DNS.
- domainName string
- dnsSearchList []string
- dnsIPv6 string
- dnsIPv4 string
-}
-
-// ovn represents a LXD OVN network.
-type ovn struct {
- common
-}
-
-// getOVNVars returns OVN object variables generated from current config.
-func (n *ovn) getOVNVars() (*ovnVars, error) {
- var err error
- v := ovnVars{}
-
- v.client = openvswitch.NewOVN()
- v.client.SetDatabaseAddress("tcp:10.109.89.169:6643") // tomp TODO we need to get this out of host OVS or LXD daemon config.
-
- // Initialise network object names.
- netName := fmt.Sprintf("lxd-net%d", n.id)
-
- // Chassis group.
- v.chassisGroupName = openvswitch.OVNChassisGroup(netName)
-
- // Router network objects (for connecting to both external and internal switches).
- v.routerName = openvswitch.OVNRouter(fmt.Sprintf("%s-lr", netName))
- v.routerExtPortName = openvswitch.OVNRouterPort(fmt.Sprintf("%s-lrp-ext", v.routerName))
- v.routerIntPortName = openvswitch.OVNRouterPort(fmt.Sprintf("%s-lrp-int", v.routerName))
- routerMAC := n.config["bridge.hwaddr"]
- if routerMAC == "" {
- routerMAC = n.config["volatile.bridge.hwaddr"]
- }
-
- v.routerMAC, err = net.ParseMAC(routerMAC)
- if err != nil {
- return nil, errors.Wrapf(err, "Failed parsing router MAC address %q", routerMAC)
- }
-
- // Load parent network.
- parentNet, err := LoadByName(n.state, n.config["parent"])
- if err != nil {
- return nil, errors.Wrapf(err, "Failead loading parent network")
- }
- parentNetConf := parentNet.Config()
-
- // Parent derived settings.
- v.extSwitchProviderName = parentNet.Name()
-
- parentIPv4, parentIPv4Mask, err := net.ParseCIDR(parentNetConf["ipv4.address"])
- if err == nil {
- v.dnsIPv4 = parentIPv4.String()
- v.routerExtGwIPv4 = parentIPv4.String()
- }
-
- parentIPv6, parentIPv6Mask, err := net.ParseCIDR(parentNetConf["ipv6.address"])
- if err == nil {
- v.dnsIPv6 = parentIPv6.String()
- v.routerExtGwIPv6 = parentIPv6.String()
- }
-
- // Allocate (or retrieve existing allocation) for the router's external IP on the parent network.
- opts := &dhcpalloc.Options{
- ProjectName: project.Default,
- HostName: netName,
- HostMAC: v.routerMAC,
- Network: parentNet,
- }
-
- err = dhcpalloc.AllocateTask(opts, func(t *dhcpalloc.Transaction) error {
- if parentIPv4 != nil {
- routerExtPortIPv4, err := t.AllocateIPv4()
-
- // If DHCP not supported, skip error, and will result in total protocol filter.
- if err != nil && err != dhcpalloc.ErrDHCPNotSupported {
- return err
- }
-
- routerExtPortIPv4Net := &net.IPNet{
- Mask: parentIPv4Mask.Mask,
- IP: routerExtPortIPv4,
- }
- v.routerExtPortIPv4Net = routerExtPortIPv4Net.String()
- }
-
- if parentIPv6 != nil {
- routerExtPortIPv6, err := t.AllocateIPv6()
-
- // If DHCP not supported, skip error, and will result in total protocol filter.
- if err != nil && err != dhcpalloc.ErrDHCPNotSupported {
- return err
- }
-
- routerExtPortIPv6Net := &net.IPNet{
- Mask: parentIPv6Mask.Mask,
- IP: routerExtPortIPv6,
- }
- v.routerExtPortIPv6Net = routerExtPortIPv6Net.String()
- }
-
- return nil
- })
- if err != nil {
- return nil, err
- }
-
- // Router settings.
- v.routerIntPortIPv4Net = n.config["ipv4.address"]
- v.routerIntPortIPv6Net = n.config["ipv6.address"]
-
- // Domain settings.
- v.domainName = "lxd"
- if n.config["dns.domain"] != "" {
- v.domainName = n.config["dns.domain"]
- }
-
- v.dnsSearchList = []string{"dns.domain"}
- if n.config["dns.search"] != "" {
- v.dnsSearchList = []string{}
- for _, domain := range strings.SplitN(n.config["dns.search"], ",", -1) {
- v.dnsSearchList = append(v.dnsSearchList, strings.TrimSpace(domain))
- }
- }
-
- // External network objects (for connecting logical router and external network).
- v.extSwitchName = openvswitch.OVNSwitch(fmt.Sprintf("%s-ls-ext", netName))
- v.extSwitchRouterPortName = openvswitch.OVNSwitchPort(fmt.Sprintf("%s-lsp-router", v.extSwitchName))
- v.extSwitchProviderPortName = openvswitch.OVNSwitchPort(fmt.Sprintf("%s-lsp-provider", v.extSwitchName))
-
- // Internal network objects (for connecting logical router and instance NICs).
- v.intSwitchName = openvswitch.OVNSwitch(fmt.Sprintf("%s-ls-int", netName))
- v.intSwitchRouterPortName = openvswitch.OVNSwitchPort(fmt.Sprintf("%s-lsp-router", v.intSwitchName))
- v.intSwitchInstancePortPrefix = fmt.Sprintf("%s-instance", netName)
-
- return &v, nil
-}
-
-// Validate network config.
-func (n *ovn) Validate(config map[string]string) error {
- rules := map[string]func(value string) error{
- "parent": func(value string) error {
- if err := ValidNetworkName(value); err != nil {
- return errors.Wrapf(err, "Invalid network name %q", value)
- }
-
- return nil
- },
- "maas.subnet.ipv4": validate.IsAny,
- "maas.subnet.ipv6": validate.IsAny,
- "bridge.hwaddr": func(value string) error {
- if value == "" {
- return nil
- }
-
- return validate.IsNetworkMAC(value)
- },
- "volatile.bridge.hwaddr": func(value string) error {
- if value == "" {
- return nil
- }
-
- return validate.IsNetworkMAC(value)
- },
- "ipv4.address": func(value string) error {
- if validate.IsOneOf(value, []string{"auto"}) == nil {
- return nil
- }
-
- return validate.Optional(validate.IsNetworkAddressCIDRV4)(value)
- },
- "ipv6.address": func(value string) error {
- if validate.IsOneOf(value, []string{"auto"}) == nil {
- return nil
- }
-
- return validate.IsNetworkAddressCIDRV6(value)
- },
- "dns.domain": validate.IsAny,
- "dns.search": validate.IsAny,
- }
-
- err := n.validate(config, rules)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// fillConfig fills requested config with any default values.
-func (n *ovn) fillConfig(config map[string]string) error {
- if config["ipv4.address"] == "" {
- config["ipv4.address"] = "auto"
- }
-
- if config["ipv6.address"] == "" {
- content, err := ioutil.ReadFile("/proc/sys/net/ipv6/conf/default/disable_ipv6")
- if err == nil && string(content) == "0\n" {
- config["ipv6.address"] = "auto"
- }
- }
-
- // If no static hwaddr specified generate a volatile one to store in DB record so that
- // there are no races when starting the network at the same time on multiple cluster nodes.
- err := n.fillHwaddr(config)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// fillHwaddr populates the volatile.bridge.hwaddr in config if it, nor bridge.hwaddr, are already set.
-func (n *ovn) fillHwaddr(config map[string]string) error {
- if config["bridge.hwaddr"] != "" || config["volatile.bridge.hwaddr"] != "" {
- return nil
- }
-
- // If no existing MAC address, generate a new one and store in volatile.
- hwAddr, err := instance.DeviceNextInterfaceHWAddr()
- if err != nil {
- return errors.Wrapf(err, "Failed generating MAC address")
- }
-
- config["volatile.bridge.hwaddr"] = hwAddr
- return nil
-}
-
-// Create sets up network in OVN Northbound database.
-func (n *ovn) Create(clusterNotification bool) error {
- n.logger.Debug("Create", log.Ctx{"clusterNotification": clusterNotification, "config": n.config})
-
- // We only need to setup the OVN Northbound database once, not on every clustered node.
- if !clusterNotification {
- err := n.setup(false)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (n *ovn) setup(update bool) error {
- // If we are in mock mode, just no-op.
- if n.state.OS.MockMode {
- return nil
- }
-
- n.logger.Debug("Setting up network")
-
- revert := revert.New()
- defer revert.Fail()
-
- var routerExtPortIPv4, routerIntPortIPv4, routerExtPortIPv6, routerIntPortIPv6 net.IP
- var routerExtPortIPv4Net, routerIntPortIPv4Net, routerExtPortIPv6Net, routerIntPortIPv6Net *net.IPNet
-
- ovn, err := n.getOVNVars()
- if err != nil {
- return err
- }
-
- // Parse router IP config.
- if ovn.routerExtPortIPv4Net != "" {
- routerExtPortIPv4, routerExtPortIPv4Net, err = net.ParseCIDR(ovn.routerExtPortIPv4Net)
- if err != nil {
- return err
- }
- }
-
- if ovn.routerExtPortIPv6Net != "" {
- routerExtPortIPv6, routerExtPortIPv6Net, err = net.ParseCIDR(ovn.routerExtPortIPv6Net)
- if err != nil {
- return err
- }
- }
-
- if ovn.routerIntPortIPv4Net != "" {
- routerIntPortIPv4, routerIntPortIPv4Net, err = net.ParseCIDR(ovn.routerIntPortIPv4Net)
- if err != nil {
- return err
- }
- }
-
- if ovn.routerIntPortIPv6Net != "" {
- routerIntPortIPv6, routerIntPortIPv6Net, err = net.ParseCIDR(ovn.routerIntPortIPv6Net)
- if err != nil {
- return err
- }
- }
-
- // Create chassis group.
- err = ovn.client.ChassisGroupAdd(ovn.chassisGroupName, update)
- if err != nil {
- return err
- }
-
- revert.Add(func() { ovn.client.ChassisGroupDelete(ovn.chassisGroupName) })
-
- // Create logical router.
- if update {
- ovn.client.LogicalRouterDelete(ovn.routerName)
- }
-
- err = ovn.client.LogicalRouterAdd(ovn.routerName)
- if err != nil {
- return errors.Wrapf(err, "Failed adding router")
- }
- revert.Add(func() { ovn.client.LogicalRouterDelete(ovn.routerName) })
-
- // Configure logical router.
-
- // Add default routes.
- if ovn.routerExtGwIPv4 != "" {
- err = ovn.client.LogicalRouterRouteAdd(ovn.routerName, &net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}, net.ParseIP(ovn.routerExtGwIPv4))
- if err != nil {
- return errors.Wrapf(err, "Failed adding IPv4 default route")
- }
- }
-
- if ovn.routerExtGwIPv6 != "" {
- err = ovn.client.LogicalRouterRouteAdd(ovn.routerName, &net.IPNet{IP: net.IPv6zero, Mask: net.CIDRMask(0, 128)}, net.ParseIP(ovn.routerExtGwIPv6))
- if err != nil {
- return errors.Wrapf(err, "Failed adding IPv6 default route")
- }
- }
-
- // Add SNAT rules.
- if routerIntPortIPv4Net != nil {
- err = ovn.client.LogicalRouterSNATAdd(ovn.routerName, routerIntPortIPv4Net, routerExtPortIPv4)
- if err != nil {
- return err
- }
- }
-
- if routerIntPortIPv6Net != nil {
- err = ovn.client.LogicalRouterSNATAdd(ovn.routerName, routerIntPortIPv6Net, routerExtPortIPv6)
- if err != nil {
- return err
- }
- }
-
- // Create external logical switch.
- if update {
- ovn.client.LogicalSwitchDelete(ovn.extSwitchName)
- }
-
- err = ovn.client.LogicalSwitchAdd(ovn.extSwitchName, false)
- if err != nil {
- return errors.Wrapf(err, "Failed adding external switch")
- }
- revert.Add(func() { ovn.client.LogicalSwitchDelete(ovn.extSwitchName) })
-
- // Generate external router port IPs (in CIDR format).
- extRouterIPs := []*net.IPNet{}
- if routerExtPortIPv4Net != nil {
- extRouterIPs = append(extRouterIPs, &net.IPNet{
- IP: routerExtPortIPv4,
- Mask: routerExtPortIPv4Net.Mask,
- })
- }
-
- if routerExtPortIPv6Net != nil {
- extRouterIPs = append(extRouterIPs, &net.IPNet{
- IP: routerExtPortIPv6,
- Mask: routerExtPortIPv6Net.Mask,
- })
- }
-
- // Create external router port.
- err = ovn.client.LogicalRouterPortAdd(ovn.routerName, ovn.routerExtPortName, ovn.routerMAC, extRouterIPs...)
- if err != nil {
- return errors.Wrapf(err, "Failed adding external router port")
- }
- revert.Add(func() { ovn.client.LogicalRouterPortDelete(ovn.routerExtPortName) })
-
- // Create external switch port and link to router port.
- err = ovn.client.LogicalSwitchPortAdd(ovn.extSwitchName, ovn.extSwitchRouterPortName, false)
- if err != nil {
- return errors.Wrapf(err, "Failed adding external switch router port")
- }
- revert.Add(func() { ovn.client.LogicalSwitchPortDelete(ovn.extSwitchRouterPortName) })
-
- err = ovn.client.LogicalSwitchPortLinkRouter(ovn.extSwitchRouterPortName, ovn.routerExtPortName)
- if err != nil {
- return errors.Wrapf(err, "Failed linking external router port to external switch port")
- }
-
- // Create external switch port and link to external provider network.
- err = ovn.client.LogicalSwitchPortAdd(ovn.extSwitchName, ovn.extSwitchProviderPortName, false)
- if err != nil {
- return errors.Wrapf(err, "Failed adding external switch provider port")
- }
- revert.Add(func() { ovn.client.LogicalSwitchPortDelete(ovn.extSwitchProviderPortName) })
-
- err = ovn.client.LogicalSwitchPortLinkProviderNetwork(ovn.extSwitchProviderPortName, ovn.extSwitchProviderName)
- if err != nil {
- return errors.Wrapf(err, "Failed linking external switch provider port to external provider network")
- }
-
- // Create internal logical switch if not updating.
- err = ovn.client.LogicalSwitchAdd(ovn.intSwitchName, update)
- if err != nil {
- return errors.Wrapf(err, "Failed adding internal switch")
- }
- revert.Add(func() { ovn.client.LogicalSwitchDelete(ovn.intSwitchName) })
-
- // Setup IP allocation config on logical switch.
- err = ovn.client.LogicalSwitchSetIPAllocation(ovn.intSwitchName, &openvswitch.OVNIPAllocationOpts{
- PrefixIPv4: routerIntPortIPv4Net,
- PrefixIPv6: routerIntPortIPv6Net,
- ExcludeIPv4: []openvswitch.OVNIPRange{{Start: routerIntPortIPv4}},
- })
- if err != nil {
- return errors.Wrapf(err, "Failed setting IP allocation settings on internal switch")
- }
-
- // Find MAC address for internal router port.
-
- var dhcpv4UUID, dhcpv6UUID string
-
- if update {
- // Find first existing DHCP options set for IPv4 and IPv6 and update them instead of adding sets.
- existingOpts, err := ovn.client.LogicalSwitchDHCPOptionsGet(ovn.intSwitchName)
- if err != nil {
- return errors.Wrapf(err, "Failed getting existing DHCP settings for internal switch")
- }
-
- for _, existingOpt := range existingOpts {
- if existingOpt.CIDR.IP.To4() == nil {
- if dhcpv6UUID == "" {
- dhcpv6UUID = existingOpt.UUID
- }
- } else {
- if dhcpv4UUID == "" {
- dhcpv4UUID = existingOpt.UUID
- }
- }
- }
- }
-
- // Create DHCPv4 options for internal switch.
- err = ovn.client.LogicalSwitchDHCPv4OptionsSet(ovn.intSwitchName, dhcpv4UUID, routerIntPortIPv4Net, &openvswitch.OVNDHCPv4Opts{
- ServerID: routerIntPortIPv4,
- ServerMAC: ovn.routerMAC,
- Router: routerIntPortIPv4,
- RecursiveDNSServer: net.ParseIP(ovn.dnsIPv4),
- DomainName: ovn.domainName,
- LeaseTime: time.Duration(time.Hour * 1),
- })
- if err != nil {
- return errors.Wrapf(err, "Failed adding DHCPv4 settings for internal switch")
- }
-
- // Create DHCPv6 options for internal switch.
- err = ovn.client.LogicalSwitchDHCPv6OptionsSet(ovn.intSwitchName, dhcpv6UUID, routerIntPortIPv6Net, &openvswitch.OVNDHCPv6Opts{
- ServerID: ovn.routerMAC,
- RecursiveDNSServer: net.ParseIP(ovn.dnsIPv6),
- DNSSearchList: ovn.dnsSearchList,
- })
- if err != nil {
- return errors.Wrapf(err, "Failed adding DHCPv6 settings for internal switch")
- }
-
- // Generate internal router port IPs (in CIDR format).
- intRouterIPs := []*net.IPNet{}
- if routerIntPortIPv4Net != nil {
- intRouterIPs = append(intRouterIPs, &net.IPNet{
- IP: routerIntPortIPv4,
- Mask: routerIntPortIPv4Net.Mask,
- })
- }
-
- if routerIntPortIPv6Net != nil {
- intRouterIPs = append(intRouterIPs, &net.IPNet{
- IP: routerIntPortIPv6,
- Mask: routerIntPortIPv6Net.Mask,
- })
- }
-
- // Create internal router port.
- err = ovn.client.LogicalRouterPortAdd(ovn.routerName, ovn.routerIntPortName, ovn.routerMAC, intRouterIPs...)
- if err != nil {
- return errors.Wrapf(err, "Failed adding internal router port")
- }
- revert.Add(func() { ovn.client.LogicalRouterPortDelete(ovn.routerIntPortName) })
-
- // Set IPv6 router advertisement settings.
- if routerIntPortIPv6Net != nil {
- err = ovn.client.LogicalRouterPortSetIPv6Advertisements(ovn.routerIntPortName, &openvswitch.OVNIPv6RAOpts{
- AddressMode: openvswitch.OVNIPv6AddressModeSLAAC,
- SendPeriodic: true,
- DNSSearchList: ovn.dnsSearchList,
- RecursiveDNSServer: net.ParseIP(ovn.dnsIPv6),
-
- // Keep these low until we support DNS search domains via DHCPv4, as otherwise RA DNSSL
- // won't take effect until advert after DHCPv4 has run on instance.
- MinInterval: time.Duration(time.Second * 30),
- MaxInterval: time.Duration(time.Minute * 1),
- })
- if err != nil {
- return errors.Wrapf(err, "Failed setting internal router port IPv6 advertisement settings")
- }
- }
-
- // Create internal switch port and link to router port.
- err = ovn.client.LogicalSwitchPortAdd(ovn.intSwitchName, ovn.intSwitchRouterPortName, update)
- if err != nil {
- return errors.Wrapf(err, "Failed adding internal switch router port")
- }
- revert.Add(func() { ovn.client.LogicalSwitchPortDelete(ovn.intSwitchRouterPortName) })
-
- err = ovn.client.LogicalSwitchPortLinkRouter(ovn.intSwitchRouterPortName, ovn.routerIntPortName)
- if err != nil {
- return errors.Wrapf(err, "Failed linking internal router port to internal switch port")
- }
-
- revert.Success()
- return nil
-}
-
-// Delete deletes a network.
-func (n *ovn) Delete(clusterNotification bool) error {
- n.logger.Debug("Delete", log.Ctx{"clusterNotification": clusterNotification})
-
- if !clusterNotification {
- ovn, err := n.getOVNVars()
- if err != nil {
- return err
- }
-
- ovn.client.ChassisGroupDelete(ovn.chassisGroupName)
- ovn.client.LogicalRouterDelete(ovn.routerName)
- ovn.client.LogicalSwitchDelete(ovn.extSwitchName)
- ovn.client.LogicalSwitchDelete(ovn.intSwitchName)
- ovn.client.LogicalRouterPortDelete(ovn.routerExtPortName)
- ovn.client.LogicalRouterPortDelete(ovn.routerIntPortName)
- ovn.client.LogicalSwitchPortDelete(ovn.extSwitchRouterPortName)
- ovn.client.LogicalSwitchPortDelete(ovn.extSwitchProviderPortName)
- ovn.client.LogicalSwitchPortDelete(ovn.intSwitchRouterPortName)
- }
-
- return n.common.delete(clusterNotification)
-}
-
-// Rename renames a network.
-func (n *ovn) Rename(newName string) error {
- n.logger.Debug("Rename", log.Ctx{"newName": newName})
-
- // Sanity checks.
- inUse, err := n.IsUsed()
- if err != nil {
- return err
- }
-
- if inUse {
- return fmt.Errorf("The network is currently in use")
- }
-
- // Rename common steps.
- err = n.common.rename(newName)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// Start starts is a no-op.
-func (n *ovn) Start() error {
- if n.status == api.NetworkStatusPending {
- return fmt.Errorf("Cannot start pending network")
- }
-
- return nil
-}
-
-// Stop stops is a no-op.
-func (n *ovn) Stop() error {
- return nil
-}
-
-// Update updates the network. Accepts notification boolean indicating if this update request is coming from a
-// cluster notification, in which case do not update the database, just apply local changes needed.
-func (n *ovn) Update(newNetwork api.NetworkPut, targetNode string, clusterNotification bool) error {
- n.logger.Debug("Update", log.Ctx{"clusterNotification": clusterNotification, "newNetwork": newNetwork})
-
- // Populate default values if they are missing.
- err := n.fillConfig(newNetwork.Config)
- if err != nil {
- return err
- }
-
- // Populate auto fields.
- err = fillAuto(newNetwork.Config)
- if err != nil {
- return err
- }
-
- dbUpdateNeeeded, changedKeys, oldNetwork, err := n.common.configChanged(newNetwork)
- if err != nil {
- return err
- }
-
- if !dbUpdateNeeeded {
- return nil // Nothing changed.
- }
-
- revert := revert.New()
- defer revert.Fail()
-
- // Define a function which reverts everything.
- revert.Add(func() {
- // Reset changes to all nodes and database.
- n.common.update(oldNetwork, targetNode, clusterNotification)
-
- // Reset any change that was made to network.
- if !clusterNotification {
- n.setup(true)
- }
- })
-
- // Apply changes to database.
- err = n.common.update(newNetwork, targetNode, clusterNotification)
- if err != nil {
- return err
- }
-
- // Restart the network if needed.
- if len(changedKeys) > 0 && !clusterNotification {
- err = n.setup(true)
- if err != nil {
- return err
- }
- }
-
- revert.Success()
- return nil
-}
-
-// getInstanceDevicePortName returns the switch port name to use for an instance device.
-func (n *ovn) getInstanceDevicePortName(v *ovnVars, instanceID int, deviceName string) openvswitch.OVNSwitchPort {
- return openvswitch.OVNSwitchPort(fmt.Sprintf("%s-%d-%s", v.intSwitchInstancePortPrefix, instanceID, deviceName))
-}
-
-// instanceDevicePortAdd adds an instance device port to the internal logical switch and returns the port name.
-func (n *ovn) instanceDevicePortAdd(instanceID int, deviceName string, mac net.HardwareAddr, ips []net.IP) (openvswitch.OVNSwitchPort, error) {
- var dhcpV4ID, dhcpv6ID string
-
- revert := revert.New()
- defer revert.Fail()
-
- ovn, err := n.getOVNVars()
- if err != nil {
- return "", err
- }
-
- // Get DHCP options IDs.
- if ovn.routerIntPortIPv4Net != "" {
- _, routerIntPortIPv4Net, err := net.ParseCIDR(ovn.routerIntPortIPv4Net)
- if err != nil {
- return "", err
- }
-
- dhcpV4ID, err = ovn.client.LogicalSwitchDHCPOptionsGetID(ovn.intSwitchName, routerIntPortIPv4Net)
- if err != nil {
- return "", err
- }
- }
-
- if ovn.routerIntPortIPv6Net != "" {
- _, routerIntPortIPv6Net, err := net.ParseCIDR(ovn.routerIntPortIPv6Net)
- if err != nil {
- return "", err
- }
-
- dhcpv6ID, err = ovn.client.LogicalSwitchDHCPOptionsGetID(ovn.intSwitchName, routerIntPortIPv6Net)
- if err != nil {
- return "", err
- }
- }
-
- instancePortName := n.getInstanceDevicePortName(ovn, instanceID, deviceName)
-
- // Add port with mayExist set to true, so that if instance port exists, we don't fail and continue below
- // to configure the port as needed. This is required in case the OVN northbound database was unavailable
- // when the instance NIC was stopped and was unable to remove the port on last stop, which would otherwise
- // prevent future NIC starts.
- err = ovn.client.LogicalSwitchPortAdd(ovn.intSwitchName, instancePortName, true)
- if err != nil {
- return "", err
- }
-
- revert.Add(func() { ovn.client.LogicalSwitchPortDelete(instancePortName) })
-
- err = ovn.client.LogicalSwitchPortSet(instancePortName, &openvswitch.OVNSwitchPortOpts{
- DHCPv4OptsID: dhcpV4ID,
- DHCPv6OptsID: dhcpv6ID,
- MAC: mac,
- IPs: ips,
- })
- if err != nil {
- return "", err
- }
-
- revert.Success()
- return instancePortName, nil
-}
-
-// instanceDevicePortDelete deletes an instance device port from the internal logical switch.
-func (n *ovn) instanceDevicePortDelete(instanceID int, deviceName string) error {
- ovn, err := n.getOVNVars()
- if err != nil {
- return err
- }
-
- instancePortName := n.getInstanceDevicePortName(ovn, instanceID, deviceName)
-
- err = ovn.client.LogicalSwitchPortDelete(instancePortName)
- if err != nil {
- return err
- }
-
- return nil
-}
From 651cd93557f4ff4ea27df68c57e48e8a482b8165 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:51:24 +0100
Subject: [PATCH 30/33] shared/validate: Makes IsNetworkAddressV6 non-optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
shared/validate/validate.go | 4 ----
1 file changed, 4 deletions(-)
diff --git a/shared/validate/validate.go b/shared/validate/validate.go
index 0856e89f0d..dba07eef8b 100644
--- a/shared/validate/validate.go
+++ b/shared/validate/validate.go
@@ -248,10 +248,6 @@ func IsNetworkV6(value string) error {
// IsNetworkAddressV6 validates an IPv6 addresss string. If string is empty, returns valid.
func IsNetworkAddressV6(value string) error {
- if value == "" {
- return nil
- }
-
ip := net.ParseIP(value)
if ip == nil || ip.To4() != nil {
return fmt.Errorf("Not an IPv6 address: %s", value)
From 1b71737b29c61308fc5de3d4a757788997377a53 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 13:57:10 +0100
Subject: [PATCH 31/33] lxd: Wraps validate.IsNetworkAddressV6 in
validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic.go | 4 ++--
lxd/device/nic_ipvlan.go | 6 +-----
lxd/device/nic_routed.go | 8 +-------
lxd/network/driver_bridge.go | 2 +-
4 files changed, 5 insertions(+), 15 deletions(-)
diff --git a/lxd/device/nic.go b/lxd/device/nic.go
index 5a15c3e4b1..a1756aa055 100644
--- a/lxd/device/nic.go
+++ b/lxd/device/nic.go
@@ -24,14 +24,14 @@ func nicValidationRules(requiredFields []string, optionalFields []string) map[st
"maas.subnet.ipv4": validate.IsAny,
"maas.subnet.ipv6": validate.IsAny,
"ipv4.address": validate.Optional(validate.IsNetworkAddressV4),
- "ipv6.address": validate.IsNetworkAddressV6,
+ "ipv6.address": validate.Optional(validate.IsNetworkAddressV6),
"ipv4.routes": validate.IsNetworkV4List,
"ipv6.routes": validate.IsNetworkV6List,
"boot.priority": validate.Optional(validate.IsUint32),
"ipv4.gateway": networkValidGateway,
"ipv6.gateway": networkValidGateway,
"ipv4.host_address": validate.Optional(validate.IsNetworkAddressV4),
- "ipv6.host_address": validate.IsNetworkAddressV6,
+ "ipv6.host_address": validate.Optional(validate.IsNetworkAddressV6),
"ipv4.host_table": validate.Optional(validate.IsUint32),
"ipv6.host_table": validate.Optional(validate.IsUint32),
}
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index e21efcfed4..fc99bd4a3b 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -121,11 +121,7 @@ func (d *nicIPVLAN) validateConfig(instConf instance.ConfigReader) error {
}
rules["ipv6.gateway"] = func(value string) error {
- if value == "" {
- return nil
- }
-
- return validate.IsNetworkAddressV6(value)
+ return validate.Optional(validate.IsNetworkAddressV6)(value)
}
}
diff --git a/lxd/device/nic_routed.go b/lxd/device/nic_routed.go
index 9a7f963e4c..7ac1b151b6 100644
--- a/lxd/device/nic_routed.go
+++ b/lxd/device/nic_routed.go
@@ -60,13 +60,7 @@ func (d *nicRouted) validateConfig(instConf instance.ConfigReader) error {
return validate.IsNetworkAddressV4List(value)
}
- rules["ipv6.address"] = func(value string) error {
- if value == "" {
- return nil
- }
-
- return validate.IsNetworkAddressV6List(value)
- }
+ rules["ipv6.address"] = validate.Optional(validate.IsNetworkAddressV6List)
err := d.config.Validate(rules)
if err != nil {
diff --git a/lxd/network/driver_bridge.go b/lxd/network/driver_bridge.go
index d96a13acfd..458819807c 100644
--- a/lxd/network/driver_bridge.go
+++ b/lxd/network/driver_bridge.go
@@ -202,7 +202,7 @@ func (n *bridge) Validate(config map[string]string) error {
"ipv6.nat.order": func(value string) error {
return validate.IsOneOf(value, []string{"before", "after"})
},
- "ipv6.nat.address": validate.IsNetworkAddressV6,
+ "ipv6.nat.address": validate.Optional(validate.IsNetworkAddressV6),
"ipv6.dhcp": validate.Optional(validate.IsBool),
"ipv6.dhcp.expiry": validate.IsAny,
"ipv6.dhcp.stateful": validate.Optional(validate.IsBool),
From d177f8414b7700d5aa37e82bd20f85e0f3db505d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 14:02:15 +0100
Subject: [PATCH 32/33] lxd/device/nic/ipvlan: validate.IsNetworkAddressVX
tweaks
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_ipvlan.go | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/lxd/device/nic_ipvlan.go b/lxd/device/nic_ipvlan.go
index fc99bd4a3b..86582085a1 100644
--- a/lxd/device/nic_ipvlan.go
+++ b/lxd/device/nic_ipvlan.go
@@ -116,13 +116,8 @@ func (d *nicIPVLAN) validateConfig(instConf instance.ConfigReader) error {
}
if d.config["mode"] == ipvlanModeL2 {
- rules["ipv4.gateway"] = func(value string) error {
- return validate.Optional(validate.IsNetworkAddressV4)(value)
- }
-
- rules["ipv6.gateway"] = func(value string) error {
- return validate.Optional(validate.IsNetworkAddressV6)(value)
- }
+ rules["ipv4.gateway"] = validate.Optional(validate.IsNetworkAddressV4)
+ rules["ipv6.gateway"] = validate.Optional(validate.IsNetworkAddressV6)
}
err := d.config.Validate(rules)
From a87bdd2a51c6a2bdc02181b0e5e159c273ae895b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Mon, 3 Aug 2020 14:02:44 +0100
Subject: [PATCH 33/33] lxd/device/nic/routed: Wraps
validate.IsNetworkAddressV4List in validate.Optional
Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
lxd/device/nic_routed.go | 8 +-------
1 file changed, 1 insertion(+), 7 deletions(-)
diff --git a/lxd/device/nic_routed.go b/lxd/device/nic_routed.go
index 7ac1b151b6..460993e639 100644
--- a/lxd/device/nic_routed.go
+++ b/lxd/device/nic_routed.go
@@ -53,13 +53,7 @@ func (d *nicRouted) validateConfig(instConf instance.ConfigReader) error {
}
rules := nicValidationRules(requiredFields, optionalFields)
- rules["ipv4.address"] = func(value string) error {
- if value == "" {
- return nil
- }
-
- return validate.IsNetworkAddressV4List(value)
- }
+ rules["ipv4.address"] = validate.Optional(validate.IsNetworkAddressV4List)
rules["ipv6.address"] = validate.Optional(validate.IsNetworkAddressV6List)
err := d.config.Validate(rules)
More information about the lxc-devel
mailing list