forked from rook/rook
This commit is a large refactor on how the operator starts, stops and how it starts various sub-components such as the ceph-csi driver. It also refines the way we cancel orchestrations. We don't use breakpoints anymore but send our self a SIGUP to reload our controller runtime manager. The reload will happen under different circonstances like: * a new adminission controller secret is created/deleted/changed * a CephCluster CR is edited As mentioned earlier, the csi driver now has its own controller, just like flex. It reacts to change in the operator config map for particular ROOK_CSI_ fields. A second new controller for the operator's general config has been created, it manages: * the logging level * the ceph CLI command timeout * the discovery daemon The operator reacts much more rapidly to cancellation events by stopping the manager's context and reloading it. Signed-off-by: Sébastien Han <seb@redhat.com>
182 lines
7.1 KiB
Go
182 lines
7.1 KiB
Go
/*
|
|
Copyright 2020 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 osd
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/rook/rook/pkg/clusterd"
|
|
"github.com/rook/rook/pkg/daemon/ceph/client"
|
|
"github.com/rook/rook/pkg/operator/ceph/cluster/osd"
|
|
"github.com/rook/rook/pkg/operator/k8sutil"
|
|
)
|
|
|
|
// RemoveOSDs purges a list of OSDs from the cluster
|
|
func RemoveOSDs(context *clusterd.Context, clusterInfo *client.ClusterInfo, osdsToRemove []string, preservePVC bool) error {
|
|
|
|
// Generate the ceph config for running ceph commands similar to the operator
|
|
if err := client.WriteCephConfig(context, clusterInfo); err != nil {
|
|
return errors.Wrap(err, "failed to write the ceph config")
|
|
}
|
|
|
|
osdDump, err := client.GetOSDDump(context, clusterInfo)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to get osd dump")
|
|
}
|
|
|
|
for _, osdIDStr := range osdsToRemove {
|
|
osdID, err := strconv.Atoi(osdIDStr)
|
|
if err != nil {
|
|
logger.Errorf("invalid OSD ID: %s. %v", osdIDStr, err)
|
|
continue
|
|
}
|
|
logger.Infof("validating status of osd.%d", osdID)
|
|
status, _, err := osdDump.StatusByID(int64(osdID))
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to get osd status for osd %d", osdID)
|
|
}
|
|
const upStatus int64 = 1
|
|
if status == upStatus {
|
|
logger.Infof("osd.%d is healthy. It cannot be removed unless it is 'down'", osdID)
|
|
continue
|
|
}
|
|
logger.Infof("osd.%d is marked 'DOWN'. Removing it", osdID)
|
|
removeOSD(context, clusterInfo, osdID, preservePVC)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func removeOSD(clusterdContext *clusterd.Context, clusterInfo *client.ClusterInfo, osdID int, preservePVC bool) {
|
|
// Get the host where the OSD is found
|
|
hostName, err := client.GetCrushHostName(clusterdContext, clusterInfo, osdID)
|
|
if err != nil {
|
|
logger.Errorf("failed to get the host where osd.%d is running. %v", osdID, err)
|
|
}
|
|
|
|
// Mark the OSD as out.
|
|
args := []string{"osd", "out", fmt.Sprintf("osd.%d", osdID)}
|
|
_, err = client.NewCephCommand(clusterdContext, clusterInfo, args).Run()
|
|
if err != nil {
|
|
logger.Errorf("failed to exclude osd.%d out of the crush map. %v", osdID, err)
|
|
}
|
|
|
|
// Remove the OSD deployment
|
|
deploymentName := fmt.Sprintf("rook-ceph-osd-%d", osdID)
|
|
deployment, err := clusterdContext.Clientset.AppsV1().Deployments(clusterInfo.Namespace).Get(clusterInfo.Context, deploymentName, metav1.GetOptions{})
|
|
if err != nil {
|
|
logger.Errorf("failed to fetch the deployment %q. %v", deploymentName, err)
|
|
} else {
|
|
logger.Infof("removing the OSD deployment %q", deploymentName)
|
|
if err := k8sutil.DeleteDeployment(clusterdContext.Clientset, clusterInfo.Namespace, deploymentName); err != nil {
|
|
if err != nil {
|
|
// Continue purging the OSD even if the deployment fails to be deleted
|
|
logger.Errorf("failed to delete deployment for OSD %d. %v", osdID, err)
|
|
}
|
|
}
|
|
if pvcName, ok := deployment.GetLabels()[osd.OSDOverPVCLabelKey]; ok {
|
|
labelSelector := fmt.Sprintf("%s=%s", osd.OSDOverPVCLabelKey, pvcName)
|
|
prepareJobList, err := clusterdContext.Clientset.BatchV1().Jobs(clusterInfo.Namespace).List(clusterInfo.Context, metav1.ListOptions{LabelSelector: labelSelector})
|
|
if err != nil && !kerrors.IsNotFound(err) {
|
|
logger.Errorf("failed to list osd prepare jobs with pvc %q. %v ", pvcName, err)
|
|
}
|
|
// Remove osd prepare job
|
|
for _, prepareJob := range prepareJobList.Items {
|
|
logger.Infof("removing the osd prepare job %q", prepareJob.GetName())
|
|
if err := k8sutil.DeleteBatchJob(clusterdContext.Clientset, clusterInfo.Namespace, prepareJob.GetName(), false); err != nil {
|
|
if err != nil {
|
|
// Continue deleting the OSD prepare job even if the deployment fails to be deleted
|
|
logger.Errorf("failed to delete prepare job for osd %q. %v", prepareJob.GetName(), err)
|
|
}
|
|
}
|
|
}
|
|
if preservePVC {
|
|
// Detach the OSD PVC from Rook. We will continue OSD deletion even if failed to remove PVC label
|
|
logger.Infof("detach the OSD PVC %q from Rook", pvcName)
|
|
if pvc, err := clusterdContext.Clientset.CoreV1().PersistentVolumeClaims(clusterInfo.Namespace).Get(clusterInfo.Context, pvcName, metav1.GetOptions{}); err != nil {
|
|
logger.Errorf("failed to get pvc for OSD %q. %v", pvcName, err)
|
|
} else {
|
|
labels := pvc.GetLabels()
|
|
delete(labels, osd.CephDeviceSetPVCIDLabelKey)
|
|
pvc.SetLabels(labels)
|
|
if _, err := clusterdContext.Clientset.CoreV1().PersistentVolumeClaims(clusterInfo.Namespace).Update(clusterInfo.Context, pvc, metav1.UpdateOptions{}); err != nil {
|
|
logger.Errorf("failed to remove label %q from pvc for OSD %q. %v", osd.CephDeviceSetPVCIDLabelKey, pvcName, err)
|
|
}
|
|
}
|
|
} else {
|
|
// Remove the OSD PVC
|
|
logger.Infof("removing the OSD PVC %q", pvcName)
|
|
if err := clusterdContext.Clientset.CoreV1().PersistentVolumeClaims(clusterInfo.Namespace).Delete(clusterInfo.Context, pvcName, metav1.DeleteOptions{}); err != nil {
|
|
if err != nil {
|
|
// Continue deleting the OSD PVC even if PVC deletion fails
|
|
logger.Errorf("failed to delete pvc for OSD %q. %v", pvcName, err)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
logger.Infof("did not find a pvc name to remove for osd %q", deploymentName)
|
|
}
|
|
}
|
|
|
|
// purge the osd
|
|
purgeosdargs := []string{"osd", "purge", fmt.Sprintf("osd.%d", osdID), "--force", "--yes-i-really-mean-it"}
|
|
_, err = client.NewCephCommand(clusterdContext, clusterInfo, purgeosdargs).Run()
|
|
if err != nil {
|
|
logger.Errorf("failed to purge osd.%d. %v", osdID, err)
|
|
}
|
|
|
|
// Attempting to remove the parent host. Errors can be ignored if there are other OSDs on the same host
|
|
hostargs := []string{"osd", "crush", "rm", hostName}
|
|
_, err = client.NewCephCommand(clusterdContext, clusterInfo, hostargs).Run()
|
|
if err != nil {
|
|
logger.Errorf("failed to remove CRUSH host %q. %v", hostName, err)
|
|
}
|
|
// call archiveCrash to silence crash warning in ceph health if any
|
|
archiveCrash(clusterdContext, clusterInfo, osdID)
|
|
|
|
logger.Infof("completed removal of OSD %d", osdID)
|
|
}
|
|
|
|
func archiveCrash(clusterdContext *clusterd.Context, clusterInfo *client.ClusterInfo, osdID int) {
|
|
// The ceph health warning should be silenced by archiving the crash
|
|
crash, err := client.GetCrash(clusterdContext, clusterInfo)
|
|
if err != nil {
|
|
logger.Errorf("failed to list ceph crash. %v", err)
|
|
return
|
|
}
|
|
if crash != nil {
|
|
logger.Info("no ceph crash to silence")
|
|
return
|
|
}
|
|
var crashID string
|
|
for _, c := range crash {
|
|
if c.Entity == fmt.Sprintf("osd.%d", osdID) {
|
|
crashID = c.ID
|
|
break
|
|
}
|
|
}
|
|
err = client.ArchiveCrash(clusterdContext, clusterInfo, crashID)
|
|
if err != nil {
|
|
logger.Errorf("failed to archive the crash %q. %v", crashID, err)
|
|
}
|
|
}
|