[lxc-devel] [lxd/master] Multi architecture clustering
freeekanayaka on Github
lxc-bot at linuxcontainers.org
Tue Jan 14 12:20:22 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 318 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200114/95b68193/attachment.bin>
-------------- next part --------------
From f3bd7c13964ca90f8510fbff7eeb08e954a5501b Mon Sep 17 00:00:00 2001
From: Dinah Baum <dinahbaum123 at gmail.com>
Date: Fri, 6 Dec 2019 13:18:41 -0600
Subject: [PATCH 1/5] api: Add clustering_architecture extension
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
doc/api-extensions.md | 4 ++++
shared/version/api.go | 1 +
2 files changed, 5 insertions(+)
diff --git a/doc/api-extensions.md b/doc/api-extensions.md
index 632ce186ac..f8daa82a8a 100644
--- a/doc/api-extensions.md
+++ b/doc/api-extensions.md
@@ -889,3 +889,7 @@ Add virtual machine support.
## image\_profiles
Allows a list of profiles to be applied to an image when launching a new container.
+
+## clustering_architecture
+This adds a new `architecture` attribute to cluster members which indicates a cluster
+member's architecture.
diff --git a/shared/version/api.go b/shared/version/api.go
index 931606c8f0..ca419947db 100644
--- a/shared/version/api.go
+++ b/shared/version/api.go
@@ -180,6 +180,7 @@ var APIExtensions = []string{
"container_disk_ceph",
"virtual-machines",
"image_profiles",
+ "clustering_architecture",
}
// APIExtensionsCount returns the number of available API extensions.
From 3ff31f78b584970ccb80624e4f35af0fd1e44374 Mon Sep 17 00:00:00 2001
From: Dinah Baum <dinahbaum123 at gmail.com>
Date: Thu, 5 Dec 2019 01:37:28 -0600
Subject: [PATCH 2/5] shared/api: Add Architecture to ClusterMember
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
shared/api/cluster.go | 3 +++
1 file changed, 3 insertions(+)
diff --git a/shared/api/cluster.go b/shared/api/cluster.go
index c773929c8d..94ac6a5016 100644
--- a/shared/api/cluster.go
+++ b/shared/api/cluster.go
@@ -60,4 +60,7 @@ type ClusterMember struct {
// API extension: clustering_roles
Roles []string `json:"roles" yaml:"roles"`
+
+ // API extension: clustering_architecture
+ Architecture string `json:"architecture" yaml:"architecture"`
}
From 13670ca842939433d6a395cbf3ff18ed4e0c6175 Mon Sep 17 00:00:00 2001
From: Dinah Baum <dinahbaum123 at gmail.com>
Date: Thu, 5 Dec 2019 01:35:20 -0600
Subject: [PATCH 3/5] lxd/db: Add Architecture to NodeInfo
Signed-off-by: Dinah Baum <dinahbaum123 at gmail.com>
---
lxd/db/node.go | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lxd/db/node.go b/lxd/db/node.go
index 7bbf423f3a..b8dc908d32 100644
--- a/lxd/db/node.go
+++ b/lxd/db/node.go
@@ -37,6 +37,7 @@ type NodeInfo struct {
APIExtensions int // Number of API extensions of the LXD code running on the node
Heartbeat time.Time // Timestamp of the last heartbeat
Roles []string // List of cluster roles
+ Architecture int // Node architecture
}
// IsOffline returns true if the last successful heartbeat time of the node is
@@ -270,6 +271,7 @@ func (c *ClusterTx) nodes(pending bool, where string, args ...interface{}) ([]No
&nodes[i].Schema,
&nodes[i].APIExtensions,
&nodes[i].Heartbeat,
+ &nodes[i].Architecture,
}
}
if pending {
@@ -279,7 +281,7 @@ func (c *ClusterTx) nodes(pending bool, where string, args ...interface{}) ([]No
}
// Get the node entries
- sql = "SELECT id, name, address, description, schema, api_extensions, heartbeat FROM nodes WHERE pending=?"
+ sql = "SELECT id, name, address, description, schema, api_extensions, heartbeat, arch FROM nodes WHERE pending=?"
if where != "" {
sql += fmt.Sprintf("AND %s ", where)
}
@@ -584,6 +586,7 @@ func (c *ClusterTx) NodeWithLeastContainers() (string, error) {
if err != nil {
return "", errors.Wrap(err, "failed to get offline threshold")
}
+
nodes, err := c.Nodes()
if err != nil {
return "", errors.Wrap(err, "failed to get current nodes")
From 5862ac84f864bf43fba7709ae89c87389fb5b0cf Mon Sep 17 00:00:00 2001
From: Free Ekanayaka <free.ekanayaka at canonical.com>
Date: Tue, 14 Jan 2020 12:12:00 +0000
Subject: [PATCH 4/5] lxd/cluster: Track member architecture
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxd/api_cluster.go | 10 +++++++++-
lxd/cluster/heartbeat_test.go | 3 ++-
lxd/cluster/membership.go | 9 +--------
lxd/cluster/membership_test.go | 7 ++++---
4 files changed, 16 insertions(+), 13 deletions(-)
diff --git a/lxd/api_cluster.go b/lxd/api_cluster.go
index bf96d39774..f37f8bf3ab 100644
--- a/lxd/api_cluster.go
+++ b/lxd/api_cluster.go
@@ -26,6 +26,7 @@ import (
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
"github.com/lxc/lxd/shared/logger"
+ "github.com/lxc/lxd/shared/osarch"
"github.com/lxc/lxd/shared/version"
)
@@ -807,6 +808,11 @@ func clusterAcceptMember(
name, address string, schema, apiExt int,
pools []api.StoragePool, networks []api.Network) (*internalClusterPostAcceptResponse, error) {
+ architecture, err := osarch.ArchitectureGetLocalID()
+ if err != nil {
+ return nil, err
+ }
+
req := internalClusterPostAcceptRequest{
Name: name,
Address: address,
@@ -814,6 +820,7 @@ func clusterAcceptMember(
API: apiExt,
StoragePools: pools,
Networks: networks,
+ Architecture: architecture,
}
info := &internalClusterPostAcceptResponse{}
resp, _, err := client.RawQuery("POST", "/internal/cluster/accept", req, "")
@@ -1047,7 +1054,7 @@ func internalClusterPostAccept(d *Daemon, r *http.Request) response.Response {
return response.SmartError(err)
}
- nodes, err := cluster.Accept(d.State(), d.gateway, req.Name, req.Address, req.Schema, req.API)
+ nodes, err := cluster.Accept(d.State(), d.gateway, req.Name, req.Address, req.Schema, req.API, req.Architecture)
if err != nil {
return response.BadRequest(err)
}
@@ -1070,6 +1077,7 @@ type internalClusterPostAcceptRequest struct {
API int `json:"api" yaml:"api"`
StoragePools []api.StoragePool `json:"storage_pools" yaml:"storage_pools"`
Networks []api.Network `json:"networks" yaml:"networks"`
+ Architecture int `json:"architecture" yaml:"architecture"`
}
// A Response for the /internal/cluster/accept endpoint.
diff --git a/lxd/cluster/heartbeat_test.go b/lxd/cluster/heartbeat_test.go
index 4b4e247dc0..b310e71b18 100644
--- a/lxd/cluster/heartbeat_test.go
+++ b/lxd/cluster/heartbeat_test.go
@@ -12,6 +12,7 @@ import (
"github.com/lxc/lxd/lxd/db"
"github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/osarch"
"github.com/lxc/lxd/shared/version"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -144,7 +145,7 @@ func (f *heartbeatFixture) Grow() *cluster.Gateway {
name := address
nodes, err := cluster.Accept(
- targetState, target, name, address, cluster.SchemaVersion, len(version.APIExtensions))
+ targetState, target, name, address, cluster.SchemaVersion, len(version.APIExtensions), osarch.ARCH_64BIT_INTEL_X86)
require.NoError(f.t, err)
err = cluster.Join(state, gateway, target.Cert(), name, nodes)
diff --git a/lxd/cluster/membership.go b/lxd/cluster/membership.go
index f2bd48eb72..9f447e41e4 100644
--- a/lxd/cluster/membership.go
+++ b/lxd/cluster/membership.go
@@ -156,7 +156,7 @@ func Bootstrap(state *state.State, gateway *Gateway, name string) error {
//
// Return an updated list raft database nodes (possibly including the newly
// accepted node).
-func Accept(state *state.State, gateway *Gateway, name, address string, schema, api int) ([]db.RaftNode, error) {
+func Accept(state *state.State, gateway *Gateway, name, address string, schema, api, arch int) ([]db.RaftNode, error) {
// Check parameters
if name == "" {
return nil, fmt.Errorf("node name must not be empty")
@@ -173,13 +173,6 @@ func Accept(state *state.State, gateway *Gateway, name, address string, schema,
return err
}
- // TODO: when fixing #6380 this should be replaced with the
- // actual architecture of the foreign node.
- arch, err := osarch.ArchitectureGetLocalID()
- if err != nil {
- return err
- }
-
// Add the new node
id, err := tx.NodeAddWithArch(name, address, arch)
if err != nil {
diff --git a/lxd/cluster/membership_test.go b/lxd/cluster/membership_test.go
index 5462f7fc3e..bf7934b105 100644
--- a/lxd/cluster/membership_test.go
+++ b/lxd/cluster/membership_test.go
@@ -13,6 +13,7 @@ import (
"github.com/lxc/lxd/lxd/db"
"github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/osarch"
"github.com/lxc/lxd/shared/version"
"github.com/mpvl/subtest"
"github.com/stretchr/testify/assert"
@@ -204,7 +205,7 @@ func TestAccept_UnmetPreconditions(t *testing.T) {
c.setup(&membershipFixtures{t: t, state: state})
- _, err := cluster.Accept(state, gateway, c.name, c.address, c.schema, c.api)
+ _, err := cluster.Accept(state, gateway, c.name, c.address, c.schema, c.api, osarch.ARCH_64BIT_INTEL_X86)
assert.EqualError(t, err, c.error)
})
}
@@ -224,7 +225,7 @@ func TestAccept(t *testing.T) {
f.ClusterNode("1.2.3.4:666")
nodes, err := cluster.Accept(
- state, gateway, "buzz", "5.6.7.8:666", cluster.SchemaVersion, len(version.APIExtensions))
+ state, gateway, "buzz", "5.6.7.8:666", cluster.SchemaVersion, len(version.APIExtensions), osarch.ARCH_64BIT_INTEL_X86)
assert.NoError(t, err)
assert.Len(t, nodes, 2)
assert.Equal(t, int64(1), nodes[0].ID)
@@ -306,7 +307,7 @@ func TestJoin(t *testing.T) {
// Accept the joining node.
raftNodes, err := cluster.Accept(
- targetState, targetGateway, "rusp", address, cluster.SchemaVersion, len(version.APIExtensions))
+ targetState, targetGateway, "rusp", address, cluster.SchemaVersion, len(version.APIExtensions), osarch.ARCH_64BIT_INTEL_X86)
require.NoError(t, err)
// Actually join the cluster.
From 777660eb18b8aedd9aee6bc6600549716799ff15 Mon Sep 17 00:00:00 2001
From: Dinah Baum <dinahbaum123 at gmail.com>
Date: Thu, 5 Dec 2019 01:33:20 -0600
Subject: [PATCH 5/5] lxc/cluster: Add architecture column in list
Signed-off-by: Free Ekanayaka <free.ekanayaka at canonical.com>
---
lxc/cluster.go | 3 ++-
lxd/cluster/membership.go | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/lxc/cluster.go b/lxc/cluster.go
index 6b0b01d879..6d013ca54e 100644
--- a/lxc/cluster.go
+++ b/lxc/cluster.go
@@ -117,7 +117,7 @@ func (c *cmdClusterList) Run(cmd *cobra.Command, args []string) error {
if member.Database {
database = "YES"
}
- line := []string{member.ServerName, member.URL, database, strings.ToUpper(member.Status), member.Message}
+ line := []string{member.ServerName, member.URL, database, strings.ToUpper(member.Status), member.Message, member.Architecture}
data = append(data, line)
}
sort.Sort(byName(data))
@@ -128,6 +128,7 @@ func (c *cmdClusterList) Run(cmd *cobra.Command, args []string) error {
i18n.G("DATABASE"),
i18n.G("STATE"),
i18n.G("MESSAGE"),
+ i18n.G("ARCHITECTURE"),
}
return utils.RenderTable(c.flagFormat, header, data, members)
diff --git a/lxd/cluster/membership.go b/lxd/cluster/membership.go
index 9f447e41e4..c857bb7788 100644
--- a/lxd/cluster/membership.go
+++ b/lxd/cluster/membership.go
@@ -811,6 +811,11 @@ func List(state *state.State) ([]api.ClusterMember, error) {
result[i].URL = fmt.Sprintf("https://%s", node.Address)
result[i].Database = shared.StringInSlice(string(db.ClusterRoleDatabase), node.Roles)
result[i].Roles = node.Roles
+ result[i].Architecture, err = osarch.ArchitectureName(node.Architecture)
+ if err != nil {
+ return nil, err
+ }
+
if node.IsOffline(offlineThreshold) {
result[i].Status = "Offline"
result[i].Message = fmt.Sprintf(
More information about the lxc-devel
mailing list