Skip to content

Commit

Permalink
feat(disk replacement, cspc): add feat to support disk replacement in…
Browse files Browse the repository at this point in the history
… CSPC Operator (#1540)

This PR add support to replace block device in cspc operator.
cspc operator will replace block device in corresponding raid group
in CSPI. CSPC Operator will replace CSPI only if one block device is
replaced by user in a raid group.

Signed-off-by: mittachaitu <[email protected]>
  • Loading branch information
sai chaithanya authored and vishnuitta committed Dec 6, 2019
1 parent f2c04ca commit e67a7b5
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 4 deletions.
163 changes: 160 additions & 3 deletions cmd/cspc-operator/app/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,33 @@ func (pc *PoolConfig) handleOperations() {
}

// replaceBlockDevice replaces block devices in cStor pools as specified in CSPC.
func (pc *PoolConfig) replaceBlockDevice() {
klog.V(2).Info("block device replacement is not supported yet")
func (pc *PoolConfig) replaceBlockDevice() error {
for _, pool := range pc.AlgorithmConfig.CSPC.Spec.Pools {
pool := pool
var cspiObj *apis.CStorPoolInstance
nodeName, err := nodeselect.GetNodeFromLabelSelector(pool.NodeSelector)
if err != nil {
return errors.Wrapf(err,
"could not get node name for node selector {%v} "+
"from cspc %s", pool.NodeSelector, pc.AlgorithmConfig.CSPC.Name)
}

cspiObj, err = pc.getCSPIWithNodeName(nodeName)
if err != nil {
return errors.Wrapf(err, "failed to get cspi with node name %s", nodeName)
}

if isPoolSpecBlockDevicesGotReplaced(&pool, cspiObj) {
pc.updateExistingCSPI(&pool, cspiObj)
_, err = apiscsp.NewKubeClient().
WithNamespace(pc.AlgorithmConfig.Namespace).
Update(cspiObj)
if err != nil {
klog.Errorf("could not replace block device in cspi %s: %s", cspiObj.Name, err.Error())
}
}
}
return nil
}

// expandPool expands the required cStor pools as specified in CSPC
Expand Down Expand Up @@ -71,7 +96,6 @@ func (pc *PoolConfig) expandPool() error {
klog.Errorf("could not update cspi %s: %s", cspiObj.Name, err.Error())
}
}

return nil
}

Expand Down Expand Up @@ -108,6 +132,28 @@ func (pc *PoolConfig) addGroupToPool(cspcPoolSpec *apis.PoolSpec, cspi *apis.CSt
return cspi
}

// updateExistingCSPI updates the CSPI object with new block devices only when
// there are changes in block devices between raid groups of CSPI and CSPC
func (pc *PoolConfig) updateExistingCSPI(
cspcPoolSpec *apis.PoolSpec, cspi *apis.CStorPoolInstance) *apis.CStorPoolInstance {
var err error
for _, cspcRaidGroup := range cspcPoolSpec.RaidGroups {
cspcRaidGroup := cspcRaidGroup
cspiRaidGroup := getReplacedCSPIRaidGroup(&cspcRaidGroup, cspi)
if cspiRaidGroup != nil {
err = pc.replaceExistingBlockDevice(cspcRaidGroup, cspiRaidGroup)
if err != nil {
klog.Infof(
"failed to replace block device in raid group type: {%v} error: %v",
cspcRaidGroup.Type,
err,
)
}
}
}
return cspi
}

// expandExistingStripedGroup adds newly added block devices to the existing striped
// groups present on CSPI
func (pc *PoolConfig) expandExistingStripedGroup(cspcPoolSpec *apis.PoolSpec, cspi *apis.CStorPoolInstance) {
Expand Down Expand Up @@ -177,6 +223,88 @@ func isRaidGroupPresentOnCSPI(group *apis.RaidGroup, cspi *apis.CStorPoolInstanc
return false
}

func (pc *PoolConfig) replaceExistingBlockDevice(
cspcRaidGroup apis.RaidGroup,
cspiRaidGroup *apis.RaidGroup) error {
cspcBlockDeviceMap := make(map[string]bool)
cspiBlockDeviceMap := make(map[string]bool)
var oldBlockDeviceName string
var newBlockDeviceName string

// Form CSPI Block Device Map
for _, bd := range cspiRaidGroup.BlockDevices {
cspiBlockDeviceMap[bd.BlockDeviceName] = true
}
// Form CSPC Block Device Map
for _, bd := range cspcRaidGroup.BlockDevices {
cspcBlockDeviceMap[bd.BlockDeviceName] = true
}
// Find Old Block Device Name
for bdName := range cspiBlockDeviceMap {
if !cspcBlockDeviceMap[bdName] {
oldBlockDeviceName = bdName
break
}
}
// Find New Block Device Name
for bdName := range cspcBlockDeviceMap {
if !cspiBlockDeviceMap[bdName] {
newBlockDeviceName = bdName
break
}
}

if oldBlockDeviceName == "" || newBlockDeviceName == "" {
return errors.Errorf(
"failed to find new block device {%s} or old block device {%s}",
oldBlockDeviceName,
newBlockDeviceName,
)
}

// Verify is that new block device is usable
err := pc.isBDUsable(newBlockDeviceName)
if err != nil {
return errors.Wrapf(
err,
"could not use bd %s for replacement",
newBlockDeviceName)
}
//Replace old block device with new block device in CSPI
for index, bd := range cspiRaidGroup.BlockDevices {
if bd.BlockDeviceName == oldBlockDeviceName {
cspiRaidGroup.BlockDevices[index].BlockDeviceName = newBlockDeviceName
return nil
}
}
return nil
}

// getReplacedCSPIRaidGroup returns the corresponding CSPI raid group for provided CSPC
// raid group only if there is one block device replacement or else it will return
// nil
func getReplacedCSPIRaidGroup(
cspcRaidGroup *apis.RaidGroup,
cspi *apis.CStorPoolInstance) *apis.RaidGroup {
blockDeviceMap := make(map[string]bool)
for _, bd := range cspcRaidGroup.BlockDevices {
blockDeviceMap[bd.BlockDeviceName] = true
}
for _, cspiRaidGroup := range cspi.Spec.RaidGroups {
cspiRaidGroup := cspiRaidGroup
misMatchedBDCount := 0
for _, cspiBD := range cspiRaidGroup.BlockDevices {
if !blockDeviceMap[cspiBD.BlockDeviceName] {
misMatchedBDCount++
}
}
if misMatchedBDCount == 1 {
return &cspiRaidGroup
}
}
return nil
}

// getAddedBlockDevicesInGroup returns the added block device list
func getAddedBlockDevicesInGroup(groupOnCSPC, groupOnCSPI *apis.RaidGroup) []string {
var addedBlockDevices []string
Expand Down Expand Up @@ -261,3 +389,32 @@ func (pc *PoolConfig) ClaimBD(bdName string) error {
}
return nil
}

// isPoolSpecBlockDevicesGotReplaced return true if any block device in CSPC pool
// spec got replaced. If no block device changes are detected then it will
// return false
func isPoolSpecBlockDevicesGotReplaced(
cspcPoolSpec *apis.PoolSpec, cspi *apis.CStorPoolInstance) bool {
cspcBlockDeviceMap := getBlockDeviceMapFromRaidGroups(cspcPoolSpec.RaidGroups)
for _, rg := range cspi.Spec.RaidGroups {
for _, bd := range rg.BlockDevices {
if !cspcBlockDeviceMap[bd.BlockDeviceName] {
return true
}
}
}
return false
}

// getBlockDeviceMapFromRaidGroups will return map of block devices that are in
// use by raid groups
func getBlockDeviceMapFromRaidGroups(
raidGroups []apis.RaidGroup) map[string]bool {
blockDeviceMap := make(map[string]bool)
for _, rg := range raidGroups {
for _, bd := range rg.BlockDevices {
blockDeviceMap[bd.BlockDeviceName] = true
}
}
return blockDeviceMap
}
2 changes: 1 addition & 1 deletion pkg/apis/openebs.io/v1alpha1/cstor_pool_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ limitations under the License.
package v1alpha1

import (
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down

0 comments on commit e67a7b5

Please sign in to comment.