Skip to content

Develop new storage driver for OpenSDS

Leon Wang edited this page Jun 20, 2019 · 5 revisions

Here is a tutorial guiding vendors and new contributors to get familiar with OpenSDS by demonstrating how to develop new block storage driver.

Create a new branch for driver

Since all the new commit will be merged to the development branch firstly, so the new driver branch should be created tracking with the development branch.

cd $GOPATH/src/github.com/opensds/opensds
git checkout -b new_driver origin/development

Code development

All drivers should be implement in contrib/drivers/.

  • create a newdriver folder.
mkdir -p contrib/drivers/newdriver
touch contrib/drivers/newdriver/driver.go
  • Develop some driver code like blow:
// Copyright 2019 The OpenSDS Authors.
//
// 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 newdriver

import (
	log "github.com/golang/glog"
	. "github.com/opensds/opensds/contrib/drivers/utils/config"
	. "github.com/opensds/opensds/pkg/model"
	pb "github.com/opensds/opensds/pkg/model/proto"
	"github.com/opensds/opensds/pkg/utils/config"
)

const (
	DefaultConfPath = "/etc/opensds/driver/new_driver.yaml"
	NamePrefix      = "opensds"
)

// These authentication structure an example, you change it depending on you requirements.
type AuthOptions struct {
	Endpoint string `yaml:"endpoint"`
	Name     string `yaml:"name,flow"`
	Password string `yaml:"password,flow"`
}

type Config struct {
	AuthOptions `yaml:"authOptions"`
	Pool        map[string]PoolProperties `yaml:"pool,flow"`
}

type Driver struct {
	conf *Config
}

func (d *Driver) Setup() error {

	conf := &Config{}
	d.conf = conf
	path := config.CONF.OsdsDock.Backends.NewDriver.ConfigPath
	if "" == path {
		path = DefaultConfPath
	}
	Parse(conf, path)
	return nil
}

func (d *Driver) Unset() error {
	return nil
}

func (d *Driver) CreateVolume(opt *pb.CreateVolumeOpts) (*VolumeSpec, error) {
	log.Info("CreateVolume ...")
	// TODO: create a volume
	return &VolumeSpec{
		BaseModel: &BaseModel{
			Id: opt.GetId(),
		},
		Name:             opt.GetName(),
		Size:             opt.Size,
		Description:      opt.GetDescription(),
		AvailabilityZone: opt.GetAvailabilityZone(),
		PoolId:           opt.GetPoolId(),
		Metadata:         nil,
	}, nil
}

func (d *Driver) PullVolume(volIdentifier string) (*VolumeSpec, error) {
	// Not used , do nothing
	return nil, nil
}

func (d *Driver) DeleteVolume(opt *pb.DeleteVolumeOpts) error {
	log.Info("DeleteVolume ...")
	// TODO: delete a volume
	return nil
}

func (d *Driver) ExtendVolume(opt *pb.ExtendVolumeOpts) (*VolumeSpec, error) {
	log.Info("ExtendVolume ...")
	// TODO: extend a volume
	return &VolumeSpec{
		BaseModel: &BaseModel{
			Id: opt.GetId(),
		},
		Name:             opt.GetName(),
		Size:             opt.GetSize(),
		Description:      opt.GetDescription(),
		AvailabilityZone: opt.GetAvailabilityZone(),
	}, nil
}

func (d *Driver) InitializeConnection(opt *pb.CreateVolumeAttachmentOpts) (*ConnectionInfo, error) {
	log.Info("InitializeConnection ...")
	// TODO: initialize a connection
	return &ConnectionInfo{
		DriverVolumeType: ISCSIProtocol,
		ConnectionData:   map[string]interface{}{},
	}, nil
}

func (d *Driver) TerminateConnection(opt *pb.DeleteVolumeAttachmentOpts) error {
	log.Info("InitializeConnection ...")
	//TODO: terminate a connection
	return nil
}

func (d *Driver) CreateSnapshot(opt *pb.CreateVolumeSnapshotOpts) (*VolumeSnapshotSpec, error) {
	log.Info("CreateSnapshot ...")
	// TODO: create a snapshot
	return &VolumeSnapshotSpec{
		BaseModel: &BaseModel{
			Id: opt.GetId(),
		},
		Name:        opt.GetName(),
		Description: opt.GetDescription(),
		VolumeId:    opt.GetVolumeId(),
		Size:        opt.GetSize(),
	}, nil
}
func (d *Driver) PullSnapshot(snapIdentifier string) (*VolumeSnapshotSpec, error) {
	// Not used, do nothing
	return nil, nil
}

func (d *Driver) DeleteSnapshot(opt *pb.DeleteVolumeSnapshotOpts) error {
	log.Info("DeleteSnapshot ...")
	// TODO: delete snapshot
	return nil
}

func (d *Driver) ListPools() ([]*StoragePoolSpec, error) {
	log.Info("ListPools ...")
	var pols []*StoragePoolSpec
	// TODO: Get all pools from actual storage backend, and
	// filter them by items that is configured in  /etc/opensds/driver/newdriver.yaml
	return pols, nil
}

// The interfaces blow are optional, so implement it or not depends on you.
func (d *Driver) InitializeSnapshotConnection(opt *pb.CreateSnapshotAttachmentOpts) (*ConnectionInfo, error) {
	return nil, &NotImplementError{S: "method InitializeSnapshotConnection has not been implemented yet."}
}

func (d *Driver) TerminateSnapshotConnection(opt *pb.DeleteSnapshotAttachmentOpts) error {
	return &NotImplementError{S: "method TerminateSnapshotConnection has not been implemented yet."}
}

func (d *Driver) CreateVolumeGroup(
	opt *pb.CreateVolumeGroupOpts,
	vg *VolumeGroupSpec) (*VolumeGroupSpec, error) {
	return nil, &NotImplementError{S: "method CreateVolumeGroup has not been implemented yet."}
}

func (d *Driver) UpdateVolumeGroup(
	opt *pb.UpdateVolumeGroupOpts,
	vg *VolumeGroupSpec,
	addVolumesRef []*VolumeSpec,
	removeVolumesRef []*VolumeSpec) (*VolumeGroupSpec, []*VolumeSpec, []*VolumeSpec, error) {
	return nil, nil, nil, &NotImplementError{S: "method UpdateVolumeGroup has not been implemented yet."}
}

func (d *Driver) DeleteVolumeGroup(
	opt *pb.DeleteVolumeGroupOpts,
	vg *VolumeGroupSpec,
	volumes []*VolumeSpec) (*VolumeGroupSpec, []*VolumeSpec, error) {
	return nil, nil, &NotImplementError{S: "method DeleteVolumeGroup has not been implemented yet."}
}
  • Add some code in contrib/drivers/driver.go, so osdsdock can find your driver.
import (
	"github.com/opensds/opensds/contrib/drivers/newdriver"
)

// Init
func Init(resourceType string) VolumeDriver {
	var d VolumeDriver
	switch resourceType {
	case config.CinderDriverType:
		d = &cinder.Driver{}
		break
	case config.CephDriverType:
		d = &ceph.Driver{}
		break
	case config.LVMDriverType:
		d = &lvm.Driver{}
		break
	case config.HuaweiDoradoDriverType:
		d = &dorado.Driver{}
		break
	case config.HuaweiFusionStorageDriverType:
		d = &fusionstorage.Driver{}
	case config.HpeNimbleDriverType:
		d = &nimble.Driver{}
		break
	// the new dirver is here
	case "new_driver":
		d = &newdriver.Driver{}
	default:
		d = &sample.Driver{}
		break
	}
	d.Setup()
	return d
}

// Clean
func Clean(d VolumeDriver) VolumeDriver {
	// Execute different clean operations according to the VolumeDriver type.
	switch d.(type) {
	case *cinder.Driver:
		break
	case *ceph.Driver:
		break
	case *lvm.Driver:
		break
	case *dorado.Driver:
		break
	case *fusionstorage.Driver:
		break
	case *nimble.Driver:
		break
	// new driver clean up operation
	case *newdriver.Driver:
		break
	default:
		break
	}
	d.Unset()
	d = nil

	return d
}
  • Add global configuration structure in ./pkg/utils/config/config_define.go
type Backends struct {
	Ceph                BackendProperties `conf:"ceph"`
	Cinder              BackendProperties `conf:"cinder"`
	Sample              BackendProperties `conf:"sample"`
	LVM                 BackendProperties `conf:"lvm"`
	HuaweiDorado        BackendProperties `conf:"huawei_dorado"`
	HuaweiFusionStorage BackendProperties `conf:"huawei_fusionstorage"`
	HpeNimble           BackendProperties `conf:"hpe_nimble"`
	NFS                 BackendProperties `conf:"nfs"`
	// new driver global configuration is here
	NewDriver BackendProperties `conf:"new_driver"`
}
  • Build the OpenSDS binary files, the output files are in the ./build/out/bin
root@ubuntu:~/gopath/src/github.com/opensds/opensds# make
mkdir -p /root/gopath/src/github.com/opensds/opensds/build/out
go build -ldflags '-w -s' -o /root/gopath/src/github.com/opensds/opensds/build/out/bin/osdsdock github.com/opensds/opensds/cmd/osdsdock
go build -ldflags '-w -s' -o /root/gopath/src/github.com/opensds/opensds/build/out/bin/osdslet github.com/opensds/opensds/cmd/osdslet
go build -ldflags '-w -s' -o /root/gopath/src/github.com/opensds/opensds/build/out/bin/osdsapiserver github.com/opensds/opensds/cmd/osdsapiserver
go build -ldflags '-w -s' -o /root/gopath/src/github.com/opensds/opensds/build/out/bin/osdsctl github.com/opensds/opensds/osdsctl
  • Edit the global configuration file to enable the 'new_driver' backend.
[osdsapiserver]
api_endpoint = 0.0.0.0:50040
auth_strategy = noauth

[osdslet]
api_endpoint = 127.0.0.1:50049

[osdsdock]
api_endpoint = 127.0.0.1:50050
# Choose the type of dock resource, only support 'provisioner' and 'attacher'.
dock_type = provisioner
# Specify which backends should be enabled, sample,ceph,cinder,lvm and so on.
enabled_backends = new_driver

# new driver section
[new_driver]
name = new_driver
description = New driver Test
driver_name = new_driver
config_path = /etc/opensds/driver/new_driver.yaml

[database]
endpoint = 127.0.0.1:2379,127.0.0.1:2380
driver = etcd
  • Add the driver self-defined yaml files ````vim /etc/opensds/driver/new_driver.yaml``. Here is an referenced configuration file based on driver.go.
authOptions:
  endpoint: 192.168.0.100
  name: opensds
  password: opensds@123

pool:
  pool001:
    storageType: block
    availabilityZone: nova-01
    extras:
      dataStorage:
        provisioningPolicy: Thin
        isSpaceEfficient: false
      ioConnectivity:
        accessProtocol: DSWARE
        maxIOPS: 7000000
        maxBWS: 600
      advanced:
        diskType: SSD
        latency: 3ms
  • Then start the OpenSDS services: osdsdock, osdslet and osdsapiserver. For debugging reason, you can start these services in terminal.
killall osdsdock osdslet osdsapiserver
./build/out/bin/osdsdock --logtostderr -daemon -v 8
./build/out/bin/osdslet --logtostderr -daemon -v 8
./build/out/bin/osdsapiserver --logtostderr -daemon -v 8
  • Check if you driver is you already up. If you find a new dock named new_driver, congratulations your driver is deployed successfully.
root@ubuntu:~/gopath/src/github.com/opensds/opensds# ./build/out/bin/osdsctl dock list
+--------------------------------------+------------+-----------------+--------------------+------------+
| Id                                   | Name       | Description     | Endpoint           | DriverName |
+--------------------------------------+------------+-----------------+--------------------+------------+
| 7acbb377-9bb1-5688-8e8b-48bfa6e48703 | new_driver | New driver Test | 127.0.0.1:50050 | new_driver |
+--------------------------------------+------------+-----------------+--------------------+------------+

Hope you could enjoy it, and more suggestions are welcomed!