Skip to content

Commit

Permalink
Merge pull request #632 from qwren/nvmeof-support
Browse files Browse the repository at this point in the history
contrib: add NVMeoF support
  • Loading branch information
leonwanghui authored Mar 27, 2019
2 parents 770d820 + b3c1855 commit ea275a7
Show file tree
Hide file tree
Showing 10 changed files with 1,038 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.Printf("Error encountered in send targets:%v, %v\n",err,info)
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:%s, %s, %s ", 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 ea275a7

Please sign in to comment.