-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #421 from codenrhoden/feature/azure
Azure Unmanaged Disk support
- Loading branch information
Showing
18 changed files
with
1,914 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
Oops, something went wrong.