Skip to content

Commit

Permalink
contrib: add NVMeoF support
Browse files Browse the repository at this point in the history
This patch will add NVMeoF support.

The NVMe over Fabrics (NVMeOF) is an emerging technology, and it gives
data centers unprecedented access to NVMe SSD storage. Like iSCSI, NVMeОF
is a network protocol used to communicate between a host and a storage
system over a network (aka fabric).

Signed-off-by: Shixin Yang <shixin.yang@intel.com>
Signed-off-by: Wenjie Liu <wenjie.liu@intel.com>
Signed-off-by: Qiaowei Ren <qiaowei.ren@intel.com>
  • Loading branch information
qwren committed Mar 21, 2019
1 parent c39ff25 commit 9e98707
Show file tree
Hide file tree
Showing 10 changed files with 1,022 additions and 32 deletions.
3 changes: 3 additions & 0 deletions contrib/connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const (
Iqn = "iqn"

RbdDriver = "rbd"

NvmeofDriver = "nvmeof"
Nqn = "nqn"
)

// Connector implementation
Expand Down
40 changes: 40 additions & 0 deletions contrib/connector/nvmeof/nvmeof.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) 2019 Intel Corporation. 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 nvmeof

import (
"github.com/opensds/opensds/contrib/connector"
)

type Nvmeof struct{}

func init() {
connector.RegisterConnector(connector.NvmeofDriver, &Nvmeof{})
connector.ExecCmd("modprobe", "nvme-rdma")
}

func (nof *Nvmeof) Attach(conn map[string]interface{}) (string, error) {
return Connect(conn)
}

func (nof *Nvmeof) Detach(conn map[string]interface{}) error {
NvmeofCon := ParseNvmeofConnectInfo(conn)
return DisConnect(NvmeofCon.Nqn)
}

// GetInitiatorInfo implementation
func (nof *Nvmeof) GetInitiatorInfo() (string, error) {
return getInitiatorInfo()
}
199 changes: 199 additions & 0 deletions contrib/connector/nvmeof/nvmeof_helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Copyright (c) 2019 Intel Corporation, Ltd. 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 nvmeof

import (
"errors"
"log"
"strings"
"time"

"github.com/mitchellh/mapstructure"
"github.com/opensds/opensds/contrib/connector"
)

const (
iniNvmePrefix = "nqn.ini."
)

// ConnectorInfo define
type ConnectorInfo struct {
Nqn string `mapstructure:"targetNQN"` //NVMe subsystem name to the volume to be connected
TgtPort string `mapstructure:"targetPort"` //NVMe target port that hosts the nqn sybsystem
TgtPortal string `mapstructure:"targetIP"` //NVMe target ip that hosts the nqn sybsystem
TranType string `mapstructure:"transporType "` // Nvme transport type
HostNqn string `mapstructure:"hostNqn"` // host nqn
}

//////////////////////////////////////////////////////////////////////////////////////////
// Refer some codes from: https://github.intel.com/yingxinc/cinder-rsd-os-brick //
//////////////////////////////////////////////////////////////////////////////////////////

// GetInitiator returns all the Nvmeof UUID
func GetInitiator() ([]string, error) {
res, err := connector.ExecCmd("dmidecode")
nqns := []string{}
if err != nil {
log.Printf("Unable to execute dmidecode,Error encountered gathering Nvmeof UUID: %v\n", err)
return nqns, nil
}

lines := strings.Split(string(res), "\n")
for _, l := range lines {
if strings.Contains(l, "UUID:") {
tmp := iniNvmePrefix + strings.Split(l, ":")[1]
nqns = append(nqns, tmp)
log.Printf("Found the following nqns: %s", nqns)
return nqns, nil
}
}
log.Println("can not find any nqn initiator")
return nqns, errors.New("can not find any nqn initiator")
}

func getInitiatorInfo() (string, error) {

initiators, err := GetInitiator()
if err != nil {
return "", err
}

if len(initiators) == 0 {
return "", errors.New("no nqn found")
}

if len(initiators) > 1 {
return "", errors.New("the number of nqn is wrong")
}

hostName, err := connector.GetHostName()
if err != nil {
return "", errors.New("can not get hostname")
}

info := initiators[0] + "." + hostName
return info, nil
}

// GetNvmeDevice get all the nvme devices
func GetNvmeDevice() (map[string]int, error) {
nvmeDevice := make(map[string]int)
pattern := "/dev/nvme"
Npath, err := connector.ExecCmd("nvme", "list")
if err != nil {
return nvmeDevice, err
}
log.Println("nvme list succeed")
lines := strings.Split(string(Npath), "\n")
for _, l := range lines {
if strings.Contains(l, pattern) {
name := strings.Split(l, " ")[0]
nvmeDevice[name] = 1
}
}
return nvmeDevice, err
}

// GetNvmeSubsystems :list connected target name
func GetNvmeSubsystems() (map[string]int, error) {
nqn := make(map[string]int)
res, err := connector.ExecCmd("nvme", "list-subsys")
if err != nil {
return nqn, err
}

lines := strings.Split(string(res), "\n")
for _, l := range lines {
if strings.Contains(l, "NQN=") {
name := strings.Split(l, "NQN=")[1]
nqn[name] = 1
}
}

log.Printf("Found the following NQN: %s", res)
return nqn, nil
}

// Discovery NVMe-OF target
func Discovery(connMap map[string]interface{}) error {
conn := ParseNvmeofConnectInfo(connMap)
targetip := conn.TgtPortal
targetport := conn.TgtPort
info, err := connector.ExecCmd("nvme", "discover", "-t", "rdma", "-a", targetip, "-s", targetport)
if err != nil {
log.Println("Error encountered in send targets:%s, err: %v", string(info), err)
return err
}
return nil
}

// Connect NVMe-OF Target ,return the new target device path in this node
func Connect(connMap map[string]interface{}) (string, error) {
CurrentNvmeDevice, _ := GetNvmeDevice()
conn := ParseNvmeofConnectInfo(connMap)
connNqn := conn.Nqn
targetPortal := conn.TgtPortal
port := conn.TgtPort
nvmeTransportType := "rdma"
log.Printf("conn information: ", connNqn, ",", targetPortal, ",", port)

_, err := connector.ExecCmd("nvme", "connect", "-t",
nvmeTransportType, "-n", connNqn, "-a", targetPortal, "-s", port)
if err != nil {
log.Println("Failed to connect to NVMe nqn :", connNqn)
return "", err
}

for retry := 0; retry < 10; retry++ {
allNvmeDevices, _ := GetNvmeDevice()
for p, _ := range allNvmeDevices {
if _, ok := CurrentNvmeDevice[p]; !ok {
log.Printf("NVMe device to be connected to is : %v", p)
return p, nil
}
time.Sleep(time.Second)
}
}
return "", errors.New("could not connect volume: Timeout after 10s")
}

// DisConnect nvme device by name
func DisConnect(nqn string) error {
currentNvmeNames, err := GetNvmeSubsystems()
if err != nil {
log.Println("can not get nvme device")
return err
}
if _, ok := currentNvmeNames[nqn]; !ok {
log.Println("Trying to disconnect nqn" + nqn +
"is not connected.")
return errors.New("device path not found ")
}

_, err = connector.ExecCmd("nvme", "disconnect", "-n", nqn)
if err != nil {
log.Println("could not disconnect nvme nqn : ", nqn)
return err
}
log.Println(" disconnect nvme nqn : ", nqn)
return nil
}

// ParseNvmeofConnectInfo decode
func ParseNvmeofConnectInfo(connectInfo map[string]interface{}) *ConnectorInfo {
var con ConnectorInfo
mapstructure.Decode(connectInfo, &con)
return &con
}
40 changes: 33 additions & 7 deletions contrib/drivers/lvm/lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const (
snapshotPrefix = "_snapshot-"
blocksize = 4096
sizeShiftBit = 30
opensdsnvmepool = "opensds-nvmegroup"
nvmeofAccess = "nvmeof"
iscsiAccess = "iscsi"
)

const (
Expand Down Expand Up @@ -249,23 +252,28 @@ func (d *Driver) InitializeConnection(opt *pb.CreateVolumeAttachmentOpts) (*mode
if d.conf.EnableChapAuth {
chapAuth = []string{utils.RandSeqWithAlnum(20), utils.RandSeqWithAlnum(16)}
}
t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir)

// create target according to the pool's access protocol
accPro := opt.AccessProtocol
log.Info("accpro:", accPro)
t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir, accPro)
expt, err := t.CreateExport(opt.GetVolumeId(), lvPath, hostIP, initiator, chapAuth)
if err != nil {
log.Error("Failed to initialize connection of logic volume:", err)
return nil, err
}

return &model.ConnectionInfo{
DriverVolumeType: ISCSIProtocol,
DriverVolumeType: accPro,
ConnectionData: expt,
}, nil
}

func (d *Driver) TerminateConnection(opt *pb.DeleteVolumeAttachmentOpts) error {
t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir)
accPro := opt.AccessProtocol
t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir, accPro)
if err := t.RemoveExport(opt.GetVolumeId()); err != nil {
log.Error("Failed to initialize connection of logic volume:", err)
log.Error("failed to terminate connection of logic volume:", err)
return err
}
return nil
Expand Down Expand Up @@ -406,6 +414,11 @@ func (d *Driver) CreateSnapshot(opt *pb.CreateVolumeSnapshotOpts) (snap *model.V
metadata := map[string]string{KLvsPath: lvsPath}

if bucket, ok := opt.Metadata["bucket"]; ok {
//nvmet right now can not support snap volume serve as nvme target
if vg == opensdsnvmepool {
log.Infof("nvmet right now can not support snap volume serve as nvme target")
log.Infof("still store in nvme pool but initialize connection by iscsi protocol")
}
mountPoint, info, err := d.AttachSnapshot(opt.GetId(), lvsPath)
if err != nil {
d.cli.Delete(snapName, vg)
Expand Down Expand Up @@ -524,21 +537,34 @@ func (d *Driver) InitializeSnapshotConnection(opt *pb.CreateSnapshotAttachmentOp
chapAuth = []string{utils.RandSeqWithAlnum(20), utils.RandSeqWithAlnum(16)}
}

t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir)
accPro := opt.AccessProtocol
if accPro == nvmeofAccess {
log.Infof("nvmet right now can not support snap volume serve as nvme target")
log.Infof("still create snapshot connection by iscsi")
accPro = iscsiAccess
}
t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir, accPro)
data, err := t.CreateExport(opt.GetSnapshotId(), lvsPath, hostIP, initiator, chapAuth)
if err != nil {
log.Error("Failed to initialize snapshot connection of logic volume:", err)
return nil, err
}

return &model.ConnectionInfo{
DriverVolumeType: ISCSIProtocol,
DriverVolumeType: accPro,
ConnectionData: data,
}, nil
}

func (d *Driver) TerminateSnapshotConnection(opt *pb.DeleteSnapshotAttachmentOpts) error {
t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir)
accPro := opt.AccessProtocol
if accPro == nvmeofAccess{
log.Infof("nvmet right now can not support snap volume serve as nvme target")
log.Infof("still create snapshot connection by iscsi")
accPro = iscsiAccess
}
log.Info("terminate snapshot conn")
t := targets.NewTarget(d.conf.TgtBindIp, d.conf.TgtConfDir, accPro)
if err := t.RemoveExport(opt.GetSnapshotId()); err != nil {
log.Error("Failed to terminate snapshot connection of logic volume:", err)
return err
Expand Down
Loading

0 comments on commit 9e98707

Please sign in to comment.