To help with code readability, include daemons from `pkg/daemon/ceph` as `<dmn>daemon "github.com/rook/rook/pkg/daemon/ceph/<dmn>`. This will make it easier to understand code lines without needing to scroll up to check the includes, as there are mon/mds/mgr/osd/rgw packages for daemons as well as operator and cmd. Signed-off-by: Blaine Gardner <blaine.gardner@suse.com>
244 lines
8.4 KiB
Go
244 lines
8.4 KiB
Go
/*
|
|
Copyright 2016 The Rook Authors. All rights reserved.
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
package ceph
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/rook/rook/cmd/rook/rook"
|
|
"github.com/rook/rook/pkg/daemon/ceph/client"
|
|
mondaemon "github.com/rook/rook/pkg/daemon/ceph/mon"
|
|
osddaemon "github.com/rook/rook/pkg/daemon/ceph/osd"
|
|
"github.com/rook/rook/pkg/operator/ceph/cluster"
|
|
oposd "github.com/rook/rook/pkg/operator/ceph/cluster/osd"
|
|
osdcfg "github.com/rook/rook/pkg/operator/ceph/cluster/osd/config"
|
|
"github.com/rook/rook/pkg/operator/k8sutil"
|
|
"github.com/rook/rook/pkg/util/flags"
|
|
"github.com/spf13/cobra"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
)
|
|
|
|
var osdCmd = &cobra.Command{
|
|
Use: "osd",
|
|
Short: "Provisions and runs the osd daemon",
|
|
Hidden: true,
|
|
}
|
|
var osdConfigCmd = &cobra.Command{
|
|
Use: "config",
|
|
Short: "Updates ceph.conf for the osd",
|
|
Hidden: true,
|
|
}
|
|
var provisionCmd = &cobra.Command{
|
|
Use: "provision",
|
|
Short: "Generates osd config and prepares an osd for runtime",
|
|
Hidden: true,
|
|
}
|
|
var filestoreDeviceCmd = &cobra.Command{
|
|
Use: "filestore-device",
|
|
Short: "Runs the ceph daemon for a filestore device",
|
|
Hidden: true,
|
|
}
|
|
var (
|
|
osdDataDeviceFilter string
|
|
ownerRefID string
|
|
mountSourcePath string
|
|
mountPath string
|
|
osdID int
|
|
)
|
|
|
|
func addOSDFlags(command *cobra.Command) {
|
|
addOSDConfigFlags(osdConfigCmd)
|
|
addOSDConfigFlags(provisionCmd)
|
|
|
|
// flags specific to provisioning
|
|
provisionCmd.Flags().StringVar(&cfg.devices, "data-devices", "", "comma separated list of devices to use for storage")
|
|
provisionCmd.Flags().StringVar(&osdDataDeviceFilter, "data-device-filter", "", "a regex filter for the device names to use, or \"all\"")
|
|
provisionCmd.Flags().StringVar(&cfg.directories, "data-directories", "", "comma separated list of directory paths to use for storage")
|
|
provisionCmd.Flags().StringVar(&cfg.metadataDevice, "metadata-device", "", "device to use for metadata (e.g. a high performance SSD/NVMe device)")
|
|
provisionCmd.Flags().BoolVar(&cfg.forceFormat, "force-format", false,
|
|
"true to force the format of any specified devices, even if they already have a filesystem. BE CAREFUL!")
|
|
|
|
// flags for generating the osd config file
|
|
osdConfigCmd.Flags().IntVar(&osdID, "osd-id", -1, "osd id for which to generate config")
|
|
|
|
// flags for running filestore on a device
|
|
filestoreDeviceCmd.Flags().StringVar(&mountSourcePath, "source-path", "", "the source path of the device to mount")
|
|
filestoreDeviceCmd.Flags().StringVar(&mountPath, "mount-path", "", "the path where the device should be mounted")
|
|
|
|
// add the subcommands to the parent osd command
|
|
osdCmd.AddCommand(osdConfigCmd)
|
|
osdCmd.AddCommand(provisionCmd)
|
|
osdCmd.AddCommand(filestoreDeviceCmd)
|
|
}
|
|
|
|
func addOSDConfigFlags(command *cobra.Command) {
|
|
command.Flags().StringVar(&ownerRefID, "cluster-id", "", "the UID of the cluster CRD that owns this cluster")
|
|
command.Flags().StringVar(&cfg.location, "location", "", "location of this node for CRUSH placement")
|
|
command.Flags().StringVar(&cfg.nodeName, "node-name", os.Getenv("HOSTNAME"), "the host name of the node")
|
|
|
|
// OSD store config flags
|
|
command.Flags().IntVar(&cfg.storeConfig.WalSizeMB, "osd-wal-size", osdcfg.WalDefaultSizeMB, "default size (MB) for OSD write ahead log (WAL) (bluestore)")
|
|
command.Flags().IntVar(&cfg.storeConfig.DatabaseSizeMB, "osd-database-size", osdcfg.DBDefaultSizeMB, "default size (MB) for OSD database (bluestore)")
|
|
command.Flags().IntVar(&cfg.storeConfig.JournalSizeMB, "osd-journal-size", osdcfg.JournalDefaultSizeMB, "default size (MB) for OSD journal (filestore)")
|
|
command.Flags().StringVar(&cfg.storeConfig.StoreType, "osd-store", "", "type of backing OSD store to use (bluestore or filestore)")
|
|
}
|
|
|
|
func init() {
|
|
addOSDFlags(osdCmd)
|
|
addCephFlags(osdCmd)
|
|
flags.SetFlagsFromEnv(osdCmd.Flags(), rook.RookEnvVarPrefix)
|
|
flags.SetFlagsFromEnv(osdConfigCmd.Flags(), rook.RookEnvVarPrefix)
|
|
flags.SetFlagsFromEnv(provisionCmd.Flags(), rook.RookEnvVarPrefix)
|
|
flags.SetFlagsFromEnv(filestoreDeviceCmd.Flags(), rook.RookEnvVarPrefix)
|
|
|
|
osdConfigCmd.RunE = writeOSDConfig
|
|
provisionCmd.RunE = prepareOSD
|
|
filestoreDeviceCmd.RunE = runFilestoreDeviceOSD
|
|
}
|
|
|
|
// Start the osd daemon for filestore running on a device
|
|
func runFilestoreDeviceOSD(cmd *cobra.Command, args []string) error {
|
|
required := []string{"source-path", "mount-path"}
|
|
if err := flags.VerifyRequiredFlags(filestoreDeviceCmd, required); err != nil {
|
|
return err
|
|
}
|
|
|
|
args = append(args, []string{
|
|
fmt.Sprintf("--public-addr=%s", cfg.NetworkInfo().PublicAddr),
|
|
fmt.Sprintf("--cluster-addr=%s", cfg.NetworkInfo().ClusterAddr),
|
|
}...)
|
|
|
|
commonOSDInit(filestoreDeviceCmd)
|
|
|
|
context := createContext()
|
|
err := osddaemon.RunFilestoreOnDevice(context, mountSourcePath, mountPath, args)
|
|
if err != nil {
|
|
rook.TerminateFatal(err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func verifyConfigFlags(configCmd *cobra.Command) error {
|
|
required := []string{"cluster-id", "node-name"}
|
|
if err := flags.VerifyRequiredFlags(configCmd, required); err != nil {
|
|
return err
|
|
}
|
|
required = []string{"cluster-name", "mon-endpoints", "mon-secret", "admin-secret"}
|
|
if err := flags.VerifyRequiredFlags(osdCmd, required); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func writeOSDConfig(cmd *cobra.Command, args []string) error {
|
|
if err := verifyConfigFlags(osdConfigCmd); err != nil {
|
|
return err
|
|
}
|
|
if osdID == -1 {
|
|
return fmt.Errorf("osd id not specified")
|
|
}
|
|
|
|
clientset, _, _, err := rook.GetClientset()
|
|
if err != nil {
|
|
rook.TerminateFatal(fmt.Errorf("failed to init k8s client. %+v\n", err))
|
|
}
|
|
|
|
context := createContext()
|
|
context.Clientset = clientset
|
|
commonOSDInit(osdConfigCmd)
|
|
locArgs, err := client.FormatLocation(cfg.location, cfg.nodeName)
|
|
if err != nil {
|
|
rook.TerminateFatal(fmt.Errorf("invalid location %s. %+v\n", cfg.location, err))
|
|
}
|
|
crushLocation := strings.Join(locArgs, " ")
|
|
kv := k8sutil.NewConfigMapKVStore(clusterInfo.Name, clientset, metav1.OwnerReference{})
|
|
|
|
if err := osddaemon.WriteConfigFile(context, &clusterInfo, kv, osdID, cfg.storeConfig, cfg.nodeName, crushLocation); err != nil {
|
|
logger.Errorf("failed to write osd config file. %+v", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Provision a device or directory for an OSD
|
|
func prepareOSD(cmd *cobra.Command, args []string) error {
|
|
if err := verifyConfigFlags(provisionCmd); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := verifyRenamedFlags(osdCmd); err != nil {
|
|
return err
|
|
}
|
|
|
|
var dataDevices string
|
|
var usingDeviceFilter bool
|
|
if osdDataDeviceFilter != "" {
|
|
if cfg.devices != "" {
|
|
return fmt.Errorf("Only one of --data-devices and --data-device-filter can be specified.")
|
|
}
|
|
|
|
dataDevices = osdDataDeviceFilter
|
|
usingDeviceFilter = true
|
|
} else {
|
|
dataDevices = cfg.devices
|
|
}
|
|
|
|
clientset, _, rookClientset, err := rook.GetClientset()
|
|
if err != nil {
|
|
rook.TerminateFatal(fmt.Errorf("failed to init k8s client. %+v\n", err))
|
|
}
|
|
|
|
context := createContext()
|
|
context.Clientset = clientset
|
|
context.RookClientset = rookClientset
|
|
commonOSDInit(provisionCmd)
|
|
|
|
locArgs, err := client.FormatLocation(cfg.location, cfg.nodeName)
|
|
if err != nil {
|
|
rook.TerminateFatal(fmt.Errorf("invalid location. %+v\n", err))
|
|
}
|
|
crushLocation := strings.Join(locArgs, " ")
|
|
|
|
forceFormat := false
|
|
ownerRef := cluster.ClusterOwnerRef(clusterInfo.Name, ownerRefID)
|
|
kv := k8sutil.NewConfigMapKVStore(clusterInfo.Name, clientset, ownerRef)
|
|
agent := osddaemon.NewAgent(context, dataDevices, usingDeviceFilter, cfg.metadataDevice, cfg.directories, forceFormat,
|
|
crushLocation, cfg.storeConfig, &clusterInfo, cfg.nodeName, kv)
|
|
|
|
err = osddaemon.Provision(context, agent)
|
|
if err != nil {
|
|
// something failed in the OSD orchestration, update the status map with failure details
|
|
status := oposd.OrchestrationStatus{
|
|
Status: oposd.OrchestrationStatusFailed,
|
|
Message: err.Error(),
|
|
}
|
|
oposd.UpdateNodeStatus(kv, cfg.nodeName, status)
|
|
|
|
rook.TerminateFatal(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func commonOSDInit(cmd *cobra.Command) {
|
|
rook.SetLogLevel()
|
|
rook.LogStartupInfo(cmd.Flags())
|
|
|
|
clusterInfo.Monitors = mondaemon.ParseMonEndpoints(cfg.monEndpoints)
|
|
}
|