-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds microceph client configuration support for rbd_cache
Signed-off-by: Utkarsh Bhatt <[email protected]>
- Loading branch information
1 parent
b975e5b
commit 019a392
Showing
28 changed files
with
1,541 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
package api | ||
|
||
import ( | ||
"context" | ||
"database/sql" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/canonical/lxd/lxd/response" | ||
"github.com/canonical/lxd/shared/logger" | ||
"github.com/canonical/microceph/microceph/api/types" | ||
"github.com/canonical/microceph/microceph/ceph" | ||
"github.com/canonical/microceph/microceph/client" | ||
"github.com/canonical/microceph/microceph/common" | ||
"github.com/canonical/microceph/microceph/database" | ||
"github.com/canonical/microcluster/rest" | ||
"github.com/canonical/microcluster/state" | ||
) | ||
|
||
// Top level client API | ||
var clientCmd = rest.Endpoint{ | ||
Path: "client", | ||
Get: rest.EndpointAction{Handler: cmdClientGet, ProxyTarget: true}, | ||
} | ||
|
||
func cmdClientGet(s *state.State, r *http.Request) response.Response { | ||
return response.EmptySyncResponse | ||
} | ||
|
||
// client configs API | ||
var clientConfigsCmd = rest.Endpoint{ | ||
Path: "client/configs", | ||
Put: rest.EndpointAction{Handler: cmdClientConfigsPut, ProxyTarget: true}, | ||
Get: rest.EndpointAction{Handler: cmdClientConfigsGet, ProxyTarget: true}, | ||
} | ||
|
||
func cmdClientConfigsGet(s *state.State, r *http.Request) response.Response { | ||
var req types.ClientConfig | ||
var configs database.ClientConfigItems | ||
|
||
err := json.NewDecoder(r.Body).Decode(&req) | ||
if err != nil { | ||
return response.InternalError(err) | ||
} | ||
|
||
if len(req.Host) > 0 { | ||
configs, err = database.ClientConfigQuery.GetAllForHost(s, req.Host) | ||
} else { | ||
configs, err = database.ClientConfigQuery.GetAll(s) | ||
} | ||
if err != nil { | ||
logger.Errorf("failed fetching client configs: %v for %v", err, req) | ||
return response.SyncResponse(false, nil) | ||
} | ||
|
||
logger.Infof("Database Response: %v", configs) | ||
|
||
return response.SyncResponse(true, configs.GetClientConfigSlice()) | ||
} | ||
|
||
// Implements the render .conf file at that particular host. | ||
func cmdClientConfigsPut(s *state.State, r *http.Request) response.Response { | ||
// Check if microceph is bootstrapped. | ||
err := s.Database.Transaction(s.Context, func(ctx context.Context, tx *sql.Tx) error { | ||
isFsid, err := database.ConfigItemExists(ctx, tx, "fsid") | ||
if err != nil || !isFsid { | ||
return fmt.Errorf("cluster is not bootstrapped yet: %v", err) | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
logger.Error(err.Error()) | ||
return response.SyncResponse(false, nil) | ||
} | ||
|
||
err = ceph.UpdateConfig(common.CephState{State: s}) | ||
if err != nil { | ||
logger.Error(err.Error()) | ||
return response.SyncResponse(false, nil) | ||
} | ||
|
||
return response.EmptySyncResponse | ||
} | ||
|
||
// client configs key API | ||
var clientConfigsKeyCmd = rest.Endpoint{ | ||
Path: "client/configs/{key}", | ||
Put: rest.EndpointAction{Handler: clientConfigsKeyPut, ProxyTarget: true}, | ||
Get: rest.EndpointAction{Handler: clientConfigsKeyGet, ProxyTarget: true}, | ||
Delete: rest.EndpointAction{Handler: clientConfigsKeyDelete, ProxyTarget: true}, | ||
} | ||
|
||
func clientConfigsKeyGet(s *state.State, r *http.Request) response.Response { | ||
var req types.ClientConfig | ||
var configs database.ClientConfigItems | ||
|
||
err := json.NewDecoder(r.Body).Decode(&req) | ||
if err != nil { | ||
return response.InternalError(err) | ||
} | ||
|
||
if len(req.Host) > 0 { | ||
configs, err = database.ClientConfigQuery.GetAllForKeyAndHost(s, req.Key, req.Host) | ||
} else { | ||
configs, err = database.ClientConfigQuery.GetAllForKey(s, req.Key) | ||
} | ||
if err != nil { | ||
logger.Errorf("failed fetching client configs: %v for %v", err, req) | ||
return response.SyncResponse(false, nil) | ||
} | ||
|
||
logger.Infof("Database Response: %v", configs) | ||
|
||
return response.SyncResponse(true, configs.GetClientConfigSlice()) | ||
} | ||
|
||
func clientConfigsKeyPut(s *state.State, r *http.Request) response.Response { | ||
var req types.ClientConfig | ||
|
||
err := json.NewDecoder(r.Body).Decode(&req) | ||
if err != nil { | ||
return response.InternalError(err) | ||
} | ||
|
||
// If new config request is for global configuration. | ||
err = database.ClientConfigQuery.AddNew(s, req.Key, req.Value, req.Host) | ||
if err != nil { | ||
return response.InternalError(err) | ||
} | ||
|
||
// Trigger /conf file update across cluster. | ||
clientConfigUpdate(s, req.Wait) | ||
|
||
return response.EmptySyncResponse | ||
} | ||
|
||
func clientConfigsKeyDelete(s *state.State, r *http.Request) response.Response { | ||
var req types.ClientConfig | ||
|
||
err := json.NewDecoder(r.Body).Decode(&req) | ||
if err != nil { | ||
return response.InternalError(err) | ||
} | ||
|
||
if len(req.Host) > 0 { | ||
err = database.ClientConfigQuery.RemoveOneForKeyAndHost(s, req.Key, req.Host) | ||
if err != nil { | ||
return response.InternalError(err) | ||
} | ||
} else { | ||
err = database.ClientConfigQuery.RemoveAllForKey(s, req.Key) | ||
if err != nil { | ||
return response.InternalError(err) | ||
} | ||
} | ||
|
||
return response.EmptySyncResponse | ||
} | ||
|
||
// Perform ordered (one after other) updation of ceph.conf across the ceph cluster. | ||
func clientConfigUpdate(s *state.State, wait bool) error { | ||
if wait { | ||
// Execute update conf synchronously | ||
err := client.SendUpdateClientConfRequestToClusterMembers(common.CephState{State: s}) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Update on current host. | ||
err = ceph.UpdateConfig(common.CephState{State: s}) | ||
if err != nil { | ||
return err | ||
} | ||
} else { // Execute update asynchronously | ||
go func() { | ||
client.SendUpdateClientConfRequestToClusterMembers(common.CephState{State: s}) | ||
ceph.UpdateConfig(common.CephState{State: s}) // Restart on current host. | ||
}() | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package types | ||
|
||
// Configs holds the key value pair | ||
type ClientConfig struct { | ||
Key string `json:"key" yaml:"key"` | ||
Value string `json:"value" yaml:"value"` | ||
Host string `json:"host" yaml:"host"` | ||
Wait bool `json:"wait" yaml:"wait"` | ||
} | ||
|
||
// Configs is a slice of configs | ||
type ClientConfigs []ClientConfig |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package ceph | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/canonical/lxd/shared/logger" | ||
"github.com/canonical/microceph/microceph/common" | ||
"github.com/canonical/microceph/microceph/database" | ||
) | ||
|
||
type ClientConfigT struct { | ||
IsCache string | ||
CacheSize string | ||
IsCacheWritethrough string | ||
CacheMaxDirty string | ||
CacheTargetDirty string | ||
} | ||
|
||
func GetClientConfigForHost(s common.StateInterface, hostname string) (ClientConfigT, error) { | ||
retval := ClientConfigT{} | ||
|
||
// Get all client configs for the current host. | ||
configs, err := database.ClientConfigQuery.GetAllForHost(s.ClusterState(), hostname) | ||
if err != nil { | ||
return ClientConfigT{}, fmt.Errorf("could not query database for client configs: %v", err) | ||
} | ||
|
||
logger.Infof("Client Configs for host %s, %v", hostname, configs) | ||
|
||
for _, config := range configs { | ||
// Populate client config table using the database values. | ||
setterTable := GetClientConfigSet() | ||
err = setFieldValue(&retval, fmt.Sprint(setterTable[config.Key]), config.Value) | ||
if err != nil { | ||
return ClientConfigT{}, fmt.Errorf("cailed object population: %v", err) | ||
} | ||
} | ||
|
||
return retval, nil | ||
} | ||
|
||
func setFieldValue(ogp *ClientConfigT, field string, value string) error { | ||
r := reflect.ValueOf(ogp) | ||
f := reflect.Indirect(r).FieldByName(field) | ||
if f.Kind() != reflect.Invalid { | ||
f.SetString(value) | ||
return nil | ||
} | ||
return fmt.Errorf("cannot set field %s", field) | ||
} | ||
|
||
func GetClientConfigSet() Set { | ||
return Set{ | ||
"rbd_cache": "IsCache", | ||
"rbd_cache_size": "CacheSize", | ||
"rbd_cache_writethrough_until_flush": "IsCacheWritethrough", | ||
"rbd_cache_max_dirty": "CacheMaxDirty", | ||
"rbd_cache_target_dirty": "CacheTargetDirty", | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.