[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