Skip to content

Commit

Permalink
Merge pull request #421 from codenrhoden/feature/azure
Browse files Browse the repository at this point in the history
Azure Unmanaged Disk support
  • Loading branch information
akutz authored Feb 21, 2017
2 parents cb80e3e + a337287 commit 80a2257
Show file tree
Hide file tree
Showing 18 changed files with 1,914 additions and 19 deletions.
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,7 @@ $1:
BUILD_TAGS="$$(BUILD_TAGS)" GOOS=$2 GOARCH=amd64 $$(MAKE) $$@
$1-clean:
rm -f $1
rm -f $(EXECUTORS_GENERATED)
GO_PHONY += $1-clean
GO_CLEAN += $1-clean
endif
Expand Down Expand Up @@ -1098,6 +1099,13 @@ test-digitalocean:
test-digitalocean-clean:
DRIVERS=digitalocean $(MAKE) clean

test-azureud:
DRIVERS=azureud $(MAKE) deps
DRIVERS=azureud $(MAKE) ./drivers/storage/azureud/tests/azureud.test

test-azureud-clean:
DRIVERS=azureud $(MAKE) clean

clean: $(GO_CLEAN)

clobber: clean $(GO_CLOBBER)
Expand Down
113 changes: 113 additions & 0 deletions drivers/storage/azureud/azureud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// +build !libstorage_storage_driver libstorage_storage_driver_azureud

package azureud

import (
gofigCore "github.com/akutz/gofig"
gofig "github.com/akutz/gofig/types"
)

const (
// Name is the provider's name.
Name = "azureud"

// TagDelimiter separates tags from volume or snapshot names
TagDelimiter = "/"

// DefaultUseHTTPS - Use https prefix by default
// or not for Azure URI's
DefaultUseHTTPS = true

// TenantIDKey is an Active Directory ID from Azure
TenantIDKey = "tenantID"

// ClientIDKey is an Application ID from Azure
ClientIDKey = "clientID"

// ClientSecretKey is a secret of the application
ClientSecretKey = "clientSecret"

// CertPathKey is a path to application certificate in case of
// authorization via certificate
CertPathKey = "certPath"

// StorageAccountKey is a name of storage account
StorageAccountKey = "storageAccount"

// StorageAccessKey is an access key of storage account
StorageAccessKey = "storageAccessKey"

// TODO: add option to pass StorageURI

// SubscriptionIDKey is an ID of subscription
SubscriptionIDKey = "subscriptionID"

// ResourceGroupKey is a name of resource group
ResourceGroupKey = "resourceGroup"

// ContainerKey is a name of container in the storage account
// ('vhds' by default)
ContainerKey = "container"

// UseHTTPSKey is a flag about use https or not for making Azure URI's
UseHTTPSKey = "useHTTPS"

// TagKey is a tag key
TagKey = "tag"
)

const (
// ConfigAzure is a config key
ConfigAzure = Name

// ConfigAzureSubscriptionIDKey is a config key
ConfigAzureSubscriptionIDKey = ConfigAzure + "." + SubscriptionIDKey

// ConfigAzureResourceGroupKey is a config key
ConfigAzureResourceGroupKey = ConfigAzure + "." + ResourceGroupKey

// ConfigAzureTenantIDKey is a config key
ConfigAzureTenantIDKey = ConfigAzure + "." + TenantIDKey

// ConfigAzureStorageAccountKey is a config key
ConfigAzureStorageAccountKey = ConfigAzure + "." + StorageAccountKey

// ConfigAzureStorageAccessKey is a config key
ConfigAzureStorageAccessKey = ConfigAzure + "." + StorageAccessKey

// ConfigAzureContainerKey is a config key
ConfigAzureContainerKey = ConfigAzure + "." + ContainerKey

// ConfigAzureClientIDKey is a config key
ConfigAzureClientIDKey = ConfigAzure + "." + ClientIDKey

// ConfigAzureClientSecretKey is a config key
ConfigAzureClientSecretKey = ConfigAzure + "." + ClientSecretKey

// ConfigAzureCertPathKey is a config key
ConfigAzureCertPathKey = ConfigAzure + "." + CertPathKey

// ConfigAzureUseHTTPSKey is a config key
ConfigAzureUseHTTPSKey = ConfigAzure + "." + UseHTTPSKey

// ConfigAzureTagKey is a config key
ConfigAzureTagKey = ConfigAzure + "." + TagKey
)

func init() {
r := gofigCore.NewRegistration("AzureUnmanagedDisk")
r.Key(gofig.String, "", "", "", ConfigAzureSubscriptionIDKey)
r.Key(gofig.String, "", "", "", ConfigAzureResourceGroupKey)
r.Key(gofig.String, "", "", "", ConfigAzureTenantIDKey)
r.Key(gofig.String, "", "", "", ConfigAzureStorageAccountKey)
r.Key(gofig.String, "", "", "", ConfigAzureStorageAccessKey)
r.Key(gofig.String, "", "vhds", "", ConfigAzureContainerKey)
r.Key(gofig.String, "", "", "", ConfigAzureClientIDKey)
r.Key(gofig.String, "", "", "", ConfigAzureClientSecretKey)
r.Key(gofig.String, "", "", "", ConfigAzureCertPathKey)
r.Key(gofig.Bool, "", DefaultUseHTTPS, "", ConfigAzureUseHTTPSKey)
r.Key(gofig.String, "", "",
"Tag prefix for Azure naming", ConfigAzureTagKey)

gofigCore.Register(r)
}
172 changes: 172 additions & 0 deletions drivers/storage/azureud/executor/azure_executor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// +build !libstorage_storage_executor libstorage_storage_executor_azureud

package executor

import (
"bufio"
"bytes"
"fmt"
"os/exec"
"regexp"
"strings"

log "github.com/Sirupsen/logrus"

gofig "github.com/akutz/gofig/types"
"github.com/akutz/goof"
"github.com/akutz/gotil"

"github.com/codedellemc/libstorage/api/registry"
"github.com/codedellemc/libstorage/api/types"
"github.com/codedellemc/libstorage/drivers/storage/azureud"
"github.com/codedellemc/libstorage/drivers/storage/azureud/utils"
)

// driver is the storage executor for the azureud storage driver.
type driver struct {
config gofig.Config
}

func init() {
registry.RegisterStorageExecutor(azureud.Name, newDriver)
}

func newDriver() types.StorageExecutor {
return &driver{}
}

func (d *driver) Init(ctx types.Context, config gofig.Config) error {
ctx.Info("azureud_executor: Init")
d.config = config
return nil
}

func (d *driver) Name() string {
return azureud.Name
}

// Supported returns a flag indicating whether or not the platform
// implementing the executor is valid for the host on which the executor
// resides.
func (d *driver) Supported(
ctx types.Context,
opts types.Store) (bool, error) {

if !gotil.FileExistsInPath("lsscsi") {
ctx.Error("lsscsi executable not found in PATH")
return false, nil
}

return utils.IsAzureInstance(ctx)
}

// InstanceID returns the instance ID from the current instance from metadata
func (d *driver) InstanceID(
ctx types.Context,
opts types.Store) (*types.InstanceID, error) {
return utils.InstanceID(ctx)
}

var errNoAvaiDevice = goof.New("no available device")
var nextDevRe = regexp.MustCompile("^/dev/" +
utils.NextDeviceInfo.Prefix +
"(" + utils.NextDeviceInfo.Pattern + ")")
var availLetters = []string{
"c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n",
"o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}

// NextDevice returns the next available device.
func (d *driver) NextDevice(
ctx types.Context,
opts types.Store) (string, error) {
// All possible device paths on Linux instances are /dev/sd[c-z]

// Find which letters are used for local devices
localDeviceNames := make(map[string]bool)

localDevices, err := d.LocalDevices(
ctx, &types.LocalDevicesOpts{Opts: opts})
if err != nil {
return "", goof.WithError("error getting local devices", err)
}
localDeviceMapping := localDevices.DeviceMap

for localDevice := range localDeviceMapping {
res := nextDevRe.FindStringSubmatch(localDevice)
if len(res) > 0 {
localDeviceNames[res[1]] = true
}
}

// Find next available letter for device path
for _, letter := range availLetters {
if localDeviceNames[letter] {
continue
}
return fmt.Sprintf(
"/dev/%s%s", utils.NextDeviceInfo.Prefix, letter), nil
}
return "", errNoAvaiDevice
}

var (
devRX = regexp.MustCompile(`^/dev/sd[c-z]$`)
scsiRx = regexp.MustCompile(`^\[\d+:\d+:\d+:(\d+)\]$`)
)

// Retrieve device paths currently attached and/or mounted
func (d *driver) LocalDevices(
ctx types.Context,
opts *types.LocalDevicesOpts) (*types.LocalDevices, error) {

// Read all of the attached devices
scsiDevs, err := getSCSIDevs()
if err != nil {
return nil, err
}

devMap := map[string]string{}

scanner := bufio.NewScanner(bytes.NewReader(scsiDevs))
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
device := fields[len(fields)-1]
if !devRX.MatchString(device) {
continue
}

matches := scsiRx.FindStringSubmatch(fields[0])
if matches == nil {
continue
}

lun := matches[1]
devMap[device] = lun
}

ld := &types.LocalDevices{Driver: d.Name()}
if len(devMap) > 0 {
ld.DeviceMap = devMap
}

ctx.WithField("devicemap", ld.DeviceMap).Debug("local devices")

return ld, nil
}

func getSCSIDevs() ([]byte, error) {

out, err := exec.Command("lsscsi").Output()
if err != nil {
if exiterr, ok := err.(*exec.ExitError); ok {
stderr := string(exiterr.Stderr)
log.Errorf("Unable to get scsi devices: %s", stderr)
return nil,
goof.Newf("Unable to get scsi devices: %s",
stderr)
}
return nil, goof.WithError("Unable to get scsci devices", err)
}

return out, nil
}
Loading

0 comments on commit 80a2257

Please sign in to comment.